Merge pull request #1071 from volsch/master

Multiple values of a HTTP header are reduced to one value.
This commit is contained in:
James Agnew 2018-11-08 13:27:36 -05:00 committed by GitHub
commit 17a7fd4285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 140 additions and 11 deletions

View File

@ -22,6 +22,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* #L%
*/
import java.io.*;
import java.util.List;
import java.util.Map.Entry;
import javax.ws.rs.core.MediaType;
@ -104,8 +105,11 @@ public class JaxRsResponse extends RestfulResponse<JaxRsRequest> {
private ResponseBuilder buildResponse(int statusCode) {
ResponseBuilder response = Response.status(statusCode);
for (Entry<String, String> header : getHeaders().entrySet()) {
response.header(header.getKey(), header.getValue());
for (Entry<String, List<String>> header : getHeaders().entrySet()) {
final String key = header.getKey();
for (String value : header.getValue()) {
response.header(key, value);
}
}
return response;
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jaxrs.server.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
@ -10,6 +11,7 @@ import java.util.Set;
import javax.ws.rs.core.Response;
import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.junit.Before;
import org.junit.Test;
@ -108,10 +110,24 @@ public class JaxRsResponseTest {
assertEquals("application/xml+fhir; charset=UTF-8", result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
}
@Test
public void addMultipleHeaderValues() throws IOException {
response.addHeader("Authorization", "Basic");
response.addHeader("Authorization", "Bearer");
response.addHeader("Cache-Control", "no-cache, no-store");
final IBaseBinary binary = new Binary();
binary.setContentType("abc");
binary.setContent(new byte[] { 1 });
final Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theSummaryMode, 200, false, false, this.request);
assertThat(result.getHeaders().get("Authorization"), Matchers.contains("Basic", "Bearer"));
assertThat(result.getHeaders().get("Cache-Control"), Matchers.contains("no-cache, no-store"));
}
private Patient createPatient() {
Patient theResource = new Patient();
theResource.setId(new IdDt(15L));
return theResource;
}
}

View File

@ -21,9 +21,7 @@ package ca.uhn.fhir.rest.server;
*/
import java.io.IOException;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.*;
import org.hl7.fhir.instance.model.api.*;
@ -35,7 +33,7 @@ public abstract class RestfulResponse<T extends RequestDetails> implements IRest
private IIdType myOperationResourceId;
private IPrimitiveType<Date> myOperationResourceLastUpdated;
private ConcurrentHashMap<String, String> theHeaders = new ConcurrentHashMap<String, String>();
private Map<String, List<String>> theHeaders = new HashMap<>();
private T theRequestDetails;
public RestfulResponse(T requestDetails) {
@ -44,14 +42,14 @@ public abstract class RestfulResponse<T extends RequestDetails> implements IRest
@Override
public void addHeader(String headerKey, String headerValue) {
this.getHeaders().put(headerKey, headerValue);
this.getHeaders().computeIfAbsent(headerKey, k -> new ArrayList<>()).add(headerValue);
}
/**
* Get the http headers
* @return the headers
*/
public ConcurrentHashMap<String, String> getHeaders() {
public Map<String, List<String>> getHeaders() {
return theHeaders;
}

View File

@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.List;
import java.util.Map.Entry;
import java.util.zip.GZIPOutputStream;
@ -75,8 +76,18 @@ public class ServletRestfulResponse extends RestfulResponse<ServletRequestDetail
private void addHeaders() {
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
getRequestDetails().getServer().addHeadersToResponse(theHttpResponse);
for (Entry<String, String> header : getHeaders().entrySet()) {
theHttpResponse.setHeader(header.getKey(), header.getValue());
for (Entry<String, List<String>> header : getHeaders().entrySet()) {
final String key = header.getKey();
boolean first = true;
for (String value : header.getValue()) {
// existing headers should be overridden
if (first) {
theHttpResponse.setHeader(key, value);
first = false;
} else {
theHttpResponse.addHeader(key, value);
}
}
}
}

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.MockSettings;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;
/**
* Unit tests of {@link RestfulResponse}.
*/
public class RestfulResponseTest {
@Test
public void addMultipleHeaderValues() {
@SuppressWarnings("unchecked")
final RestfulResponse<?> restfulResponse =
mock(RestfulResponse.class, withSettings()
.useConstructor((RequestDetails) null).defaultAnswer(CALLS_REAL_METHODS));
restfulResponse.addHeader("Authorization", "Basic");
restfulResponse.addHeader("Authorization", "Bearer");
restfulResponse.addHeader("Cache-Control", "no-cache, no-store");
assertEquals(2, restfulResponse.getHeaders().size());
assertThat(restfulResponse.getHeaders().get("Authorization"), Matchers.contains("Basic", "Bearer"));
assertThat(restfulResponse.getHeaders().get("Cache-Control"), Matchers.contains("no-cache, no-store"));
}
}

View File

@ -0,0 +1,65 @@
package ca.uhn.fhir.rest.server.servlet;
import ca.uhn.fhir.rest.server.RestfulServer;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* Unit tests of {@link ServletRestfulResponse}.
*/
public class ServletRestfulResponseTest {
@Mock
private RestfulServer server;
@Mock
private ServletOutputStream servletOutputStream;
@Mock
private HttpServletResponse servletResponse;
private ServletRequestDetails requestDetails;
private ServletRestfulResponse response;
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Before
public void init() throws IOException {
Mockito.when(servletResponse.getOutputStream()).thenReturn(servletOutputStream);
requestDetails = new ServletRequestDetails();
requestDetails.setServer(server);
requestDetails.setServletResponse(servletResponse);
response = new ServletRestfulResponse(requestDetails);
}
@Test
public void addMultipleHeaderValues() throws IOException {
final ServletRestfulResponse response = new ServletRestfulResponse(requestDetails);
response.addHeader("Authorization", "Basic");
response.addHeader("Authorization", "Bearer");
response.addHeader("Cache-Control", "no-cache, no-store");
response.getResponseWriter(200, "Status", "text/plain", "UTF-8", false);
final InOrder orderVerifier = Mockito.inOrder(servletResponse);
orderVerifier.verify(servletResponse).setHeader(eq("Authorization"), eq("Basic"));
orderVerifier.verify(servletResponse).addHeader(eq("Authorization"), eq("Bearer"));
verify(servletResponse).setHeader(eq("Cache-Control"), eq("no-cache, no-store"));
}
}