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