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