From 3a1f7b6d14b776606750a475bd031a7099527c60 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Sep 2014 00:44:30 +0200 Subject: [PATCH] Client request implementations enforce RFC 6265 (cookies in a single header) Issue: SPR-12196 (cherry picked from commit 26a93b6) --- .../HttpComponentsClientHttpRequest.java | 44 +++++++++++++------ .../SimpleBufferingClientHttpRequest.java | 36 +++++++++++---- .../SimpleStreamingClientHttpRequest.java | 19 ++------ 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java index 3e833d4459..57fb84eaab 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,6 @@ import java.net.URI; import java.util.List; import java.util.Map; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; - import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpResponse; @@ -33,6 +30,10 @@ import org.apache.http.entity.ByteArrayEntity; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.util.StringUtils; + /** * {@link org.springframework.http.client.ClientHttpRequest} implementation that uses * Apache HttpComponents HttpClient to execute requests. @@ -53,7 +54,7 @@ final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpR private final HttpContext httpContext; - public HttpComponentsClientHttpRequest(HttpClient httpClient, HttpUriRequest httpRequest, HttpContext httpContext) { + HttpComponentsClientHttpRequest(HttpClient httpClient, HttpUriRequest httpRequest, HttpContext httpContext) { this.httpClient = httpClient; this.httpRequest = httpRequest; this.httpContext = httpContext; @@ -71,15 +72,8 @@ final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpR @Override protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { - for (Map.Entry> entry : headers.entrySet()) { - String headerName = entry.getKey(); - if (!headerName.equalsIgnoreCase(HTTP.CONTENT_LEN) && - !headerName.equalsIgnoreCase(HTTP.TRANSFER_ENCODING)) { - for (String headerValue : entry.getValue()) { - this.httpRequest.addHeader(headerName, headerValue); - } - } - } + addHeaders(this.httpRequest, headers); + if (this.httpRequest instanceof HttpEntityEnclosingRequest) { HttpEntityEnclosingRequest entityEnclosingRequest = (HttpEntityEnclosingRequest) this.httpRequest; HttpEntity requestEntity = new ByteArrayEntity(bufferedOutput); @@ -89,4 +83,26 @@ final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpR return new HttpComponentsClientHttpResponse(httpResponse); } + + /** + * Add the given headers to the given HTTP request. + * @param httpRequest the request to add the headers to + * @param headers the headers to add + */ + static void addHeaders(HttpUriRequest httpRequest, HttpHeaders headers) { + for (Map.Entry> entry : headers.entrySet()) { + String headerName = entry.getKey(); + if ("Cookie".equalsIgnoreCase(headerName)) { // RFC 6265 + String headerValue = StringUtils.collectionToDelimitedString(entry.getValue(), "; "); + httpRequest.addHeader(headerName, headerValue); + } + else if (!HTTP.CONTENT_LEN.equalsIgnoreCase(headerName) && + !HTTP.TRANSFER_ENCODING.equalsIgnoreCase(headerName)) { + for (String headerValue : entry.getValue()) { + httpRequest.addHeader(headerName, headerValue); + } + } + } + } + } diff --git a/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java index c2a7864296..5456a8ea3c 100644 --- a/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,12 +26,14 @@ import java.util.Map; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.util.FileCopyUtils; +import org.springframework.util.StringUtils; /** - * {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute buffered requests. - * Created via the {@link SimpleClientHttpRequestFactory}. + * {@link ClientHttpRequest} implementation that uses standard JDK facilities to + * execute buffered requests. Created via the {@link SimpleClientHttpRequestFactory}. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 * @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod) */ @@ -63,12 +65,7 @@ final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttp @Override protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { - for (Map.Entry> entry : headers.entrySet()) { - String headerName = entry.getKey(); - for (String headerValue : entry.getValue()) { - this.connection.addRequestProperty(headerName, headerValue); - } - } + addHeaders(this.connection, headers); if (this.connection.getDoOutput() && this.outputStreaming) { this.connection.setFixedLengthStreamingMode(bufferedOutput.length); @@ -81,4 +78,25 @@ final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttp return new SimpleClientHttpResponse(this.connection); } + + /** + * Add the given headers to the given HTTP connection. + * @param connection the connection to add the headers to + * @param headers the headers to add + */ + static void addHeaders(HttpURLConnection connection, HttpHeaders headers) { + for (Map.Entry> entry : headers.entrySet()) { + String headerName = entry.getKey(); + if ("Cookie".equalsIgnoreCase(headerName)) { // RFC 6265 + String headerValue = StringUtils.collectionToDelimitedString(entry.getValue(), "; "); + connection.setRequestProperty(headerName, headerValue); + } + else { + for (String headerValue : entry.getValue()) { + connection.addRequestProperty(headerName, headerValue); + } + } + } + } + } diff --git a/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java index f01f04a615..be1a0bf412 100644 --- a/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java @@ -21,16 +21,14 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; -import java.util.List; -import java.util.Map; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.util.StreamUtils; /** - * {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute streaming requests. - * Created via the {@link SimpleClientHttpRequestFactory}. + * {@link ClientHttpRequest} implementation that uses standard JDK facilities to + * execute streaming requests. Created via the {@link SimpleClientHttpRequestFactory}. * * @author Arjen Poutsma * @since 3.0 @@ -79,22 +77,13 @@ final class SimpleStreamingClientHttpRequest extends AbstractClientHttpRequest { this.connection.setChunkedStreamingMode(this.chunkSize); } } - writeHeaders(headers); + SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers); this.connection.connect(); this.body = this.connection.getOutputStream(); } return StreamUtils.nonClosing(this.body); } - private void writeHeaders(HttpHeaders headers) { - for (Map.Entry> entry : headers.entrySet()) { - String headerName = entry.getKey(); - for (String headerValue : entry.getValue()) { - this.connection.addRequestProperty(headerName, headerValue); - } - } - } - @Override protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { try { @@ -102,7 +91,7 @@ final class SimpleStreamingClientHttpRequest extends AbstractClientHttpRequest { this.body.close(); } else { - writeHeaders(headers); + SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers); this.connection.connect(); } }