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}