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