001/*
002 * #%L
003 * HAPI FHIR - Client Framework
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.rest.client.apache;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.rest.api.Constants;
024import ca.uhn.fhir.rest.client.api.IHttpResponse;
025import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
026import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
027import ca.uhn.fhir.util.StopWatch;
028import org.apache.commons.io.IOUtils;
029import org.apache.http.Header;
030import org.apache.http.HttpEntity;
031import org.apache.http.HttpResponse;
032import org.apache.http.client.methods.CloseableHttpResponse;
033import org.apache.http.entity.ContentType;
034
035import java.io.ByteArrayInputStream;
036import java.io.IOException;
037import java.io.InputStream;
038import java.io.InputStreamReader;
039import java.io.Reader;
040import java.io.StringReader;
041import java.nio.charset.Charset;
042import java.nio.charset.StandardCharsets;
043import java.util.ArrayList;
044import java.util.HashMap;
045import java.util.List;
046import java.util.Map;
047
048/**
049 * A Http Response based on Apache. This is an adapter around the class
050 * {@link org.apache.http.HttpResponse HttpResponse}
051 *
052 * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
053 */
054public class ApacheHttpResponse extends BaseHttpResponse implements IHttpResponse {
055
056        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ApacheHttpResponse.class);
057
058        private boolean myEntityBuffered = false;
059        private byte[] myEntityBytes;
060        private final HttpResponse myResponse;
061
062        public ApacheHttpResponse(HttpResponse theResponse, StopWatch theResponseStopWatch) {
063                super(theResponseStopWatch);
064                this.myResponse = theResponse;
065        }
066
067        @Override
068        public void bufferEntity() throws IOException {
069                if (myEntityBuffered) {
070                        return;
071                }
072                try (InputStream respEntity = readEntity()) {
073                        if (respEntity != null) {
074                                this.myEntityBuffered = true;
075                                try {
076                                        this.myEntityBytes = IOUtils.toByteArray(respEntity);
077                                } catch (IllegalStateException e) {
078                                        throw new InternalErrorException(Msg.code(1478) + e);
079                                }
080                        }
081                }
082        }
083
084        @Override
085        public void close() {
086                if (myResponse instanceof CloseableHttpResponse) {
087                        try {
088                                ((CloseableHttpResponse) myResponse).close();
089                        } catch (IOException e) {
090                                ourLog.debug("Failed to close response", e);
091                        }
092                }
093        }
094
095        @Override
096        public Reader createReader() throws IOException {
097                HttpEntity entity = myResponse.getEntity();
098                if (entity == null) {
099                        return new StringReader("");
100                }
101                Charset charset = null;
102                if (entity.getContentType() != null
103                                && entity.getContentType().getElements() != null
104                                && entity.getContentType().getElements().length > 0) {
105                        ContentType ct = ContentType.get(entity);
106                        charset = ct.getCharset();
107                }
108                if (charset == null) {
109                        if (Constants.STATUS_HTTP_204_NO_CONTENT
110                                        != myResponse.getStatusLine().getStatusCode()) {
111                                ourLog.debug("Response did not specify a charset, defaulting to utf-8");
112                        }
113                        charset = StandardCharsets.UTF_8;
114                }
115
116                return new InputStreamReader(readEntity(), charset);
117        }
118
119        @Override
120        public Map<String, List<String>> getAllHeaders() {
121                Map<String, List<String>> headers = new HashMap<>();
122                if (myResponse.getAllHeaders() != null) {
123                        for (Header next : myResponse.getAllHeaders()) {
124                                String name = next.getName().toLowerCase();
125                                List<String> list = headers.computeIfAbsent(name, k -> new ArrayList<>());
126                                list.add(next.getValue());
127                        }
128                }
129                return headers;
130        }
131
132        @Override
133        public List<String> getHeaders(String theName) {
134                Header[] headers = myResponse.getHeaders(theName);
135                if (headers == null) {
136                        headers = new Header[0];
137                }
138                List<String> retVal = new ArrayList<>();
139                for (Header next : headers) {
140                        retVal.add(next.getValue());
141                }
142                return retVal;
143        }
144
145        @Override
146        public String getMimeType() {
147                ContentType ct = ContentType.get(myResponse.getEntity());
148                return ct != null ? ct.getMimeType() : null;
149        }
150
151        @Override
152        public HttpResponse getResponse() {
153                return myResponse;
154        }
155
156        @Override
157        public int getStatus() {
158                return myResponse.getStatusLine().getStatusCode();
159        }
160
161        @Override
162        public String getStatusInfo() {
163                return myResponse.getStatusLine().getReasonPhrase();
164        }
165
166        @Override
167        public InputStream readEntity() throws IOException {
168                if (this.myEntityBuffered) {
169                        return new ByteArrayInputStream(myEntityBytes);
170                } else if (myResponse.getEntity() != null) {
171                        return myResponse.getEntity().getContent();
172                } else {
173                        return null;
174                }
175        }
176}