Refactor cookie support for Apache HttpClient
Closes gh-33822
This commit is contained in:
		
							parent
							
								
									e67f892e44
								
							
						
					
					
						commit
						37243f44e8
					
				| 
						 | 
				
			
			@ -19,11 +19,9 @@ package org.springframework.http.client.reactive;
 | 
			
		|||
import java.net.URI;
 | 
			
		||||
import java.net.URISyntaxException;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import org.apache.hc.client5.http.cookie.CookieStore;
 | 
			
		||||
import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
 | 
			
		||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
 | 
			
		||||
import org.apache.hc.core5.http.ContentType;
 | 
			
		||||
import org.apache.hc.core5.http.HttpRequest;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,11 +35,13 @@ import reactor.core.publisher.Mono;
 | 
			
		|||
 | 
			
		||||
import org.springframework.core.io.buffer.DataBuffer;
 | 
			
		||||
import org.springframework.core.io.buffer.DataBufferFactory;
 | 
			
		||||
import org.springframework.http.HttpCookie;
 | 
			
		||||
import org.springframework.http.HttpHeaders;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.http.support.HttpComponentsHeadersAdapter;
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.util.CollectionUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@link ClientHttpRequest} implementation for the Apache HttpComponents HttpClient 5.x.
 | 
			
		||||
| 
						 | 
				
			
			@ -143,18 +143,26 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
 | 
			
		|||
		if (getCookies().isEmpty()) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (!CollectionUtils.isEmpty(getCookies())) {
 | 
			
		||||
			this.httpRequest.setHeader(HttpHeaders.COOKIE, serializeCookies());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		CookieStore cookieStore = this.context.getCookieStore();
 | 
			
		||||
 | 
			
		||||
		getCookies().values()
 | 
			
		||||
				.stream()
 | 
			
		||||
				.flatMap(Collection::stream)
 | 
			
		||||
				.forEach(cookie -> {
 | 
			
		||||
					BasicClientCookie clientCookie = new BasicClientCookie(cookie.getName(), cookie.getValue());
 | 
			
		||||
					clientCookie.setDomain(getURI().getHost());
 | 
			
		||||
					clientCookie.setPath(getURI().getPath());
 | 
			
		||||
					cookieStore.addCookie(clientCookie);
 | 
			
		||||
				});
 | 
			
		||||
	private String serializeCookies() {
 | 
			
		||||
		boolean first = true;
 | 
			
		||||
		StringBuilder sb = new StringBuilder();
 | 
			
		||||
		for (List<HttpCookie> cookies : getCookies().values()) {
 | 
			
		||||
			for (HttpCookie cookie : cookies) {
 | 
			
		||||
				if (!first) {
 | 
			
		||||
					sb.append("; ");
 | 
			
		||||
				}
 | 
			
		||||
				else {
 | 
			
		||||
					first = false;
 | 
			
		||||
				}
 | 
			
		||||
				sb.append(cookie.getName()).append("=").append(cookie.getValue());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return sb.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,9 +21,15 @@ import java.time.Duration;
 | 
			
		|||
import java.time.ZonedDateTime;
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
import java.time.format.DateTimeParseException;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.apache.hc.client5.http.cookie.Cookie;
 | 
			
		||||
import org.apache.hc.client5.http.cookie.CookieOrigin;
 | 
			
		||||
import org.apache.hc.client5.http.cookie.CookieSpec;
 | 
			
		||||
import org.apache.hc.client5.http.cookie.MalformedCookieException;
 | 
			
		||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
 | 
			
		||||
import org.apache.hc.core5.http.Header;
 | 
			
		||||
import org.apache.hc.core5.http.HttpResponse;
 | 
			
		||||
import org.apache.hc.core5.http.Message;
 | 
			
		||||
import org.reactivestreams.Publisher;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,23 +59,47 @@ class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
 | 
			
		|||
 | 
			
		||||
		super(HttpStatusCode.valueOf(message.getHead().getCode()),
 | 
			
		||||
				HttpHeaders.readOnlyHttpHeaders(new HttpComponentsHeadersAdapter(message.getHead())),
 | 
			
		||||
				adaptCookies(context),
 | 
			
		||||
				adaptCookies(message.getHead(), context),
 | 
			
		||||
				Flux.from(message.getBody()).map(dataBufferFactory::wrap)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static MultiValueMap<String, ResponseCookie> adaptCookies(HttpClientContext context) {
 | 
			
		||||
	private static MultiValueMap<String, ResponseCookie> adaptCookies(
 | 
			
		||||
			HttpResponse response, HttpClientContext context) {
 | 
			
		||||
 | 
			
		||||
		LinkedMultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
 | 
			
		||||
		context.getCookieStore().getCookies().forEach(cookie ->
 | 
			
		||||
				result.add(cookie.getName(),
 | 
			
		||||
						ResponseCookie.fromClientResponse(cookie.getName(), cookie.getValue())
 | 
			
		||||
								.domain(cookie.getDomain())
 | 
			
		||||
								.path(cookie.getPath())
 | 
			
		||||
								.maxAge(getMaxAgeSeconds(cookie))
 | 
			
		||||
								.secure(cookie.isSecure())
 | 
			
		||||
								.httpOnly(cookie.containsAttribute("httponly"))
 | 
			
		||||
								.sameSite(cookie.getAttribute("samesite"))
 | 
			
		||||
								.build()));
 | 
			
		||||
 | 
			
		||||
		CookieSpec cookieSpec = context.getCookieSpec();
 | 
			
		||||
		CookieOrigin cookieOrigin = context.getCookieOrigin();
 | 
			
		||||
 | 
			
		||||
		Iterator<Header> itr = response.headerIterator(HttpHeaders.SET_COOKIE);
 | 
			
		||||
		while (itr.hasNext()) {
 | 
			
		||||
			Header header = itr.next();
 | 
			
		||||
			try {
 | 
			
		||||
				List<Cookie> cookies = cookieSpec.parse(header, cookieOrigin);
 | 
			
		||||
				for (Cookie cookie : cookies) {
 | 
			
		||||
					try {
 | 
			
		||||
						cookieSpec.validate(cookie, cookieOrigin);
 | 
			
		||||
						result.add(cookie.getName(),
 | 
			
		||||
								ResponseCookie.fromClientResponse(cookie.getName(), cookie.getValue())
 | 
			
		||||
										.domain(cookie.getDomain())
 | 
			
		||||
										.path(cookie.getPath())
 | 
			
		||||
										.maxAge(getMaxAgeSeconds(cookie))
 | 
			
		||||
										.secure(cookie.isSecure())
 | 
			
		||||
										.httpOnly(cookie.containsAttribute("httponly"))
 | 
			
		||||
										.sameSite(cookie.getAttribute("samesite"))
 | 
			
		||||
										.build());
 | 
			
		||||
					}
 | 
			
		||||
					catch (final MalformedCookieException ex) {
 | 
			
		||||
						// ignore invalid cookie
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			catch (final MalformedCookieException ex) {
 | 
			
		||||
				// ignore invalid cookie
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue