Upgrade to Jetty 12
This commit upgrades Spring Framework to Jetty 12.0.1, and Reactive HTTP Client 4.0.0. Closes gh-30698
This commit is contained in:
@@ -24,10 +24,10 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.util.InputStreamResponseListener;
|
||||
import org.eclipse.jetty.client.util.OutputStreamRequestContent;
|
||||
import org.eclipse.jetty.client.InputStreamResponseListener;
|
||||
import org.eclipse.jetty.client.OutputStreamRequestContent;
|
||||
import org.eclipse.jetty.client.Request;
|
||||
import org.eclipse.jetty.client.Response;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.Request;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.http.client;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.Response;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -17,18 +17,24 @@
|
||||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntPredicate;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.reactive.client.ContentChunk;
|
||||
import org.eclipse.jetty.client.Request;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.buffer.PooledDataBuffer;
|
||||
import org.springframework.core.io.buffer.TouchableDataBuffer;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -133,19 +139,284 @@ public class JettyClientHttpConnector implements ClientHttpConnector {
|
||||
}));
|
||||
}
|
||||
|
||||
private DataBuffer toDataBuffer(ContentChunk chunk) {
|
||||
private DataBuffer toDataBuffer(Content.Chunk chunk) {
|
||||
DataBuffer delegate = this.bufferFactory.wrap(chunk.getByteBuffer());
|
||||
return new JettyDataBuffer(delegate, chunk);
|
||||
}
|
||||
|
||||
// Originally we copy due to do:
|
||||
// https://github.com/eclipse/jetty.project/issues/2429
|
||||
|
||||
// Now that the issue is marked fixed we need to replace the below with a
|
||||
// PooledDataBuffer that adapts "release()" to "succeeded()", and also
|
||||
// evaluate if the concern here is addressed.
|
||||
private static final class JettyDataBuffer implements PooledDataBuffer {
|
||||
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(chunk.buffer.capacity());
|
||||
buffer.write(chunk.buffer);
|
||||
chunk.callback.succeeded();
|
||||
return buffer;
|
||||
private final DataBuffer delegate;
|
||||
|
||||
private final Content.Chunk chunk;
|
||||
|
||||
private final AtomicInteger refCount = new AtomicInteger(1);
|
||||
|
||||
|
||||
public JettyDataBuffer(DataBuffer delegate, Content.Chunk chunk) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
Assert.notNull(chunk, "Chunk must not be null");
|
||||
|
||||
this.delegate = delegate;
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllocated() {
|
||||
return this.refCount.get() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledDataBuffer retain() {
|
||||
if (this.delegate instanceof PooledDataBuffer pooledDelegate) {
|
||||
pooledDelegate.retain();
|
||||
}
|
||||
this.chunk.retain();
|
||||
this.refCount.getAndUpdate(c -> {
|
||||
if (c != 0) {
|
||||
return c + 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release() {
|
||||
if (this.delegate instanceof PooledDataBuffer pooledDelegate) {
|
||||
pooledDelegate.release();
|
||||
}
|
||||
this.chunk.release();
|
||||
int refCount = this.refCount.updateAndGet(c -> {
|
||||
if (c != 0) {
|
||||
return c - 1;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("already released " + this);
|
||||
}
|
||||
});
|
||||
return refCount == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledDataBuffer touch(Object hint) {
|
||||
if (this.delegate instanceof TouchableDataBuffer touchableDelegate) {
|
||||
touchableDelegate.touch(hint);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// delegation
|
||||
|
||||
@Override
|
||||
public DataBufferFactory factory() {
|
||||
return this.delegate.factory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(IntPredicate predicate, int fromIndex) {
|
||||
return this.delegate.indexOf(predicate, fromIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(IntPredicate predicate, int fromIndex) {
|
||||
return this.delegate.lastIndexOf(predicate, fromIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readableByteCount() {
|
||||
return this.delegate.readableByteCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writableByteCount() {
|
||||
return this.delegate.writableByteCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int capacity() {
|
||||
return this.delegate.capacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public DataBuffer capacity(int capacity) {
|
||||
this.delegate.capacity(capacity);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer ensureWritable(int capacity) {
|
||||
this.delegate.ensureWritable(capacity);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readPosition() {
|
||||
return this.delegate.readPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer readPosition(int readPosition) {
|
||||
this.delegate.readPosition(readPosition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writePosition() {
|
||||
return this.delegate.writePosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer writePosition(int writePosition) {
|
||||
this.delegate.writePosition(writePosition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(int index) {
|
||||
return this.delegate.getByte(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte read() {
|
||||
return this.delegate.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer read(byte[] destination) {
|
||||
this.delegate.read(destination);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer read(byte[] destination, int offset, int length) {
|
||||
this.delegate.read(destination, offset, length);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer write(byte b) {
|
||||
this.delegate.write(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer write(byte[] source) {
|
||||
this.delegate.write(source);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer write(byte[] source, int offset, int length) {
|
||||
this.delegate.write(source, offset, length);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer write(DataBuffer... buffers) {
|
||||
this.delegate.write(buffers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer write(ByteBuffer... buffers) {
|
||||
this.delegate.write(buffers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public DataBuffer slice(int index, int length) {
|
||||
DataBuffer delegateSlice = this.delegate.slice(index, length);
|
||||
this.chunk.retain();
|
||||
return new JettyDataBuffer(delegateSlice, this.chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer split(int index) {
|
||||
DataBuffer delegateSplit = this.delegate.split(index);
|
||||
this.chunk.retain();
|
||||
return new JettyDataBuffer(delegateSplit, this.chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ByteBuffer asByteBuffer() {
|
||||
return this.delegate.asByteBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ByteBuffer asByteBuffer(int index, int length) {
|
||||
return this.delegate.asByteBuffer(index, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ByteBuffer toByteBuffer(int index, int length) {
|
||||
return this.delegate.toByteBuffer(index, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toByteBuffer(int srcPos, ByteBuffer dest, int destPos, int length) {
|
||||
this.delegate.toByteBuffer(srcPos, dest, destPos, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBufferIterator readableByteBuffers() {
|
||||
ByteBufferIterator delegateIterator = this.delegate.readableByteBuffers();
|
||||
return new JettyByteBufferIterator(delegateIterator, this.chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBufferIterator writableByteBuffers() {
|
||||
ByteBufferIterator delegateIterator = this.delegate.writableByteBuffers();
|
||||
return new JettyByteBufferIterator(delegateIterator, this.chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int index, int length, Charset charset) {
|
||||
return this.delegate.toString(index, length, charset);
|
||||
}
|
||||
|
||||
|
||||
private static final class JettyByteBufferIterator implements ByteBufferIterator {
|
||||
|
||||
private final ByteBufferIterator delegate;
|
||||
|
||||
private final Content.Chunk chunk;
|
||||
|
||||
|
||||
public JettyByteBufferIterator(ByteBufferIterator delegate, Content.Chunk chunk) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
Assert.notNull(chunk, "Chunk must not be null");
|
||||
|
||||
this.delegate = delegate;
|
||||
this.chunk = chunk;
|
||||
this.chunk.retain();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.delegate.close();
|
||||
this.chunk.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.delegate.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer next() {
|
||||
return this.delegate.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,20 +16,20 @@
|
||||
|
||||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.reactive.client.ContentChunk;
|
||||
import org.eclipse.jetty.client.Request;
|
||||
import org.eclipse.jetty.http.HttpCookie;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.reactive.client.ReactiveRequest;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoSink;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
@@ -92,7 +92,9 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
return Mono.<Void>create(sink -> {
|
||||
ReactiveRequest.Content content = Flux.from(body)
|
||||
.map(buffer -> toContentChunk(buffer, sink))
|
||||
.concatMapIterable(this::toContentChunks)
|
||||
.concatWith(Mono.just(Content.Chunk.EOF))
|
||||
.doOnError(sink::error)
|
||||
.as(chunks -> ReactiveRequest.Content.fromPublisher(chunks, getContentType()));
|
||||
this.builder.content(content);
|
||||
sink.success();
|
||||
@@ -111,26 +113,28 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
||||
return contentType != null ? contentType.toString() : MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
}
|
||||
|
||||
private ContentChunk toContentChunk(DataBuffer dataBuffer, MonoSink<Void> sink) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(dataBuffer.readableByteCount());
|
||||
dataBuffer.toByteBuffer(byteBuffer);
|
||||
return new ContentChunk(byteBuffer, new Callback() {
|
||||
@Override
|
||||
public void succeeded() {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable t) {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
sink.error(t);
|
||||
}
|
||||
});
|
||||
private List<Content.Chunk> toContentChunks(DataBuffer dataBuffer) {
|
||||
|
||||
List<Content.Chunk> result = new ArrayList<>(1);
|
||||
DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers();
|
||||
while (iterator.hasNext()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
boolean last = !iterator.hasNext();
|
||||
Content.Chunk chunk = Content.Chunk.from(byteBuffer, false, () -> {
|
||||
if (last) {
|
||||
iterator.close();
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
});
|
||||
result.add(chunk);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyCookies() {
|
||||
getCookies().values().stream().flatMap(Collection::stream)
|
||||
.map(cookie -> new HttpCookie(cookie.getName(), cookie.getValue()))
|
||||
.map(cookie -> HttpCookie.build(cookie.getName(), cookie.getValue()).build())
|
||||
.forEach(this.jettyRequest::cookie);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ package org.springframework.http.client.reactive;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jetty.io.ArrayByteBufferPool;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.util.ProcessorUtils;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
@@ -69,7 +69,7 @@ public class JettyResourceFactory implements InitializingBean, DisposableBean {
|
||||
|
||||
/**
|
||||
* Configure the {@link ByteBufferPool} to use.
|
||||
* <p>By default, initialized with a {@link MappedByteBufferPool}.
|
||||
* <p>By default, initialized with a {@link ArrayByteBufferPool}.
|
||||
* @param byteBufferPool the {@link ByteBuffer} pool to use
|
||||
*/
|
||||
public void setByteBufferPool(@Nullable ByteBufferPool byteBufferPool) {
|
||||
@@ -130,7 +130,7 @@ public class JettyResourceFactory implements InitializingBean, DisposableBean {
|
||||
this.executor = threadPool;
|
||||
}
|
||||
if (this.byteBufferPool == null) {
|
||||
this.byteBufferPool = new MappedByteBufferPool(2048,
|
||||
this.byteBufferPool = new ArrayByteBufferPool(0, 2048, 65536, // from HttpClient:202
|
||||
this.executor instanceof ThreadPool.SizedThreadPool sizedThreadPool ?
|
||||
sizedThreadPool.getMaxThreads() / 2 :
|
||||
ProcessorUtils.availableProcessors() * 2);
|
||||
|
||||
@@ -18,26 +18,14 @@ package org.springframework.http.server.reactive;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jakarta.servlet.AsyncContext;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.server.HttpOutput;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.ee10.servlet.HttpOutput;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.support.JettyHeadersAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ServletHttpHandlerAdapter} extension that uses Jetty APIs for writing
|
||||
@@ -51,128 +39,18 @@ import org.springframework.util.MultiValueMap;
|
||||
*/
|
||||
public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
||||
|
||||
private static final boolean jetty11Present = ClassUtils.isPresent(
|
||||
"org.eclipse.jetty.server.HttpOutput", JettyHttpHandlerAdapter.class.getClassLoader());
|
||||
|
||||
private static final boolean jetty12Present = ClassUtils.isPresent(
|
||||
"org.eclipse.jetty.ee10.servlet.HttpOutput", JettyHttpHandlerAdapter.class.getClassLoader());
|
||||
|
||||
|
||||
public JettyHttpHandlerAdapter(HttpHandler httpHandler) {
|
||||
super(httpHandler);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext context)
|
||||
throws IOException, URISyntaxException {
|
||||
|
||||
if (jetty11Present) {
|
||||
Assert.state(getServletPath() != null, "Servlet path is not initialized");
|
||||
return new Jetty11ServerHttpRequest(
|
||||
request, context, getServletPath(), getDataBufferFactory(), getBufferSize());
|
||||
}
|
||||
else {
|
||||
return super.createRequest(request, context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServletServerHttpResponse createResponse(HttpServletResponse response,
|
||||
AsyncContext context, ServletServerHttpRequest request) throws IOException {
|
||||
|
||||
if (jetty11Present) {
|
||||
return new Jetty11ServerHttpResponse(
|
||||
response, context, getDataBufferFactory(), getBufferSize(), request);
|
||||
}
|
||||
else if (jetty12Present) {
|
||||
return new Jetty12ServerHttpResponse(
|
||||
response, context, getDataBufferFactory(), getBufferSize(), request);
|
||||
}
|
||||
else {
|
||||
return super.createResponse(response, context, request);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class Jetty11ServerHttpRequest extends ServletServerHttpRequest {
|
||||
|
||||
Jetty11ServerHttpRequest(HttpServletRequest request, AsyncContext asyncContext,
|
||||
String servletPath, DataBufferFactory bufferFactory, int bufferSize)
|
||||
throws IOException, URISyntaxException {
|
||||
|
||||
super(createHeaders(request), request, asyncContext, servletPath, bufferFactory, bufferSize);
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, String> createHeaders(HttpServletRequest servletRequest) {
|
||||
Request request = getRequest(servletRequest);
|
||||
return new JettyHeadersAdapter(HttpFields.build(request.getHttpFields()));
|
||||
}
|
||||
|
||||
private static Request getRequest(HttpServletRequest request) {
|
||||
if (request instanceof Request jettyRequest) {
|
||||
return jettyRequest;
|
||||
}
|
||||
else if (request instanceof HttpServletRequestWrapper wrapper) {
|
||||
HttpServletRequest wrappedRequest = (HttpServletRequest) wrapper.getRequest();
|
||||
return getRequest(wrappedRequest);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Cannot convert [" + request.getClass() +
|
||||
"] to org.eclipse.jetty.server.Request");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class Jetty11ServerHttpResponse extends ServletServerHttpResponse {
|
||||
|
||||
Jetty11ServerHttpResponse(HttpServletResponse response, AsyncContext asyncContext,
|
||||
DataBufferFactory bufferFactory, int bufferSize, ServletServerHttpRequest request)
|
||||
throws IOException {
|
||||
|
||||
super(createHeaders(response), response, asyncContext, bufferFactory, bufferSize, request);
|
||||
}
|
||||
|
||||
private static HttpHeaders createHeaders(HttpServletResponse servletResponse) {
|
||||
Response response = getResponse(servletResponse);
|
||||
return new HttpHeaders(new JettyHeadersAdapter(response.getHttpFields()));
|
||||
}
|
||||
|
||||
private static Response getResponse(HttpServletResponse response) {
|
||||
if (response instanceof Response jettyResponse) {
|
||||
return jettyResponse;
|
||||
}
|
||||
else if (response instanceof HttpServletResponseWrapper wrapper) {
|
||||
HttpServletResponse wrappedResponse = (HttpServletResponse) wrapper.getResponse();
|
||||
return getResponse(wrappedResponse);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Cannot convert [" + response.getClass() +
|
||||
"] to org.eclipse.jetty.server.Response");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int writeToOutputStream(DataBuffer dataBuffer) throws IOException {
|
||||
if (getOutputStream() instanceof HttpOutput httpOutput) {
|
||||
int len = 0;
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
while (iterator.hasNext() && httpOutput.isReady()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
len += byteBuffer.remaining();
|
||||
httpOutput.write(byteBuffer);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
return super.writeToOutputStream(dataBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyHeaders() {
|
||||
adaptHeaders(false);
|
||||
}
|
||||
return new Jetty12ServerHttpResponse(
|
||||
response, context, getDataBufferFactory(), getBufferSize(), request);
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +66,7 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
||||
@Override
|
||||
protected int writeToOutputStream(DataBuffer dataBuffer) throws IOException {
|
||||
OutputStream output = getOutputStream();
|
||||
if (output instanceof org.eclipse.jetty.ee10.servlet.HttpOutput httpOutput) {
|
||||
if (output instanceof HttpOutput httpOutput) {
|
||||
int len = 0;
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
while (iterator.hasNext() && httpOutput.isReady()) {
|
||||
|
||||
@@ -44,8 +44,6 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
|
||||
private final HttpFields headers;
|
||||
|
||||
private static final String IMMUTABLE_HEADER_ERROR = "Immutable headers";
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@code JettyHeadersAdapter} based on the given
|
||||
@@ -65,10 +63,10 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
|
||||
@Override
|
||||
public void add(String key, @Nullable String value) {
|
||||
if (!(this.headers instanceof HttpFields.Mutable mutableHttpFields)) {
|
||||
throw new IllegalStateException(IMMUTABLE_HEADER_ERROR);
|
||||
if (value != null) {
|
||||
HttpFields.Mutable mutableHttpFields = mutableFields();
|
||||
mutableHttpFields.add(key, value);
|
||||
}
|
||||
mutableHttpFields.add(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,10 +81,13 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
|
||||
@Override
|
||||
public void set(String key, @Nullable String value) {
|
||||
if (!(this.headers instanceof HttpFields.Mutable mutableHttpFields)) {
|
||||
throw new IllegalStateException(IMMUTABLE_HEADER_ERROR);
|
||||
HttpFields.Mutable mutableHttpFields = mutableFields();
|
||||
if (value != null) {
|
||||
mutableHttpFields.put(key, value);
|
||||
}
|
||||
else {
|
||||
mutableHttpFields.remove(key);
|
||||
}
|
||||
mutableHttpFields.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,9 +140,7 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> put(String key, List<String> value) {
|
||||
if (!(this.headers instanceof HttpFields.Mutable mutableHttpFields)) {
|
||||
throw new IllegalStateException(IMMUTABLE_HEADER_ERROR);
|
||||
}
|
||||
HttpFields.Mutable mutableHttpFields = mutableFields();
|
||||
List<String> oldValues = get(key);
|
||||
mutableHttpFields.put(key, value);
|
||||
return oldValues;
|
||||
@@ -150,9 +149,7 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> remove(Object key) {
|
||||
if (!(this.headers instanceof HttpFields.Mutable mutableHttpFields)) {
|
||||
throw new IllegalStateException(IMMUTABLE_HEADER_ERROR);
|
||||
}
|
||||
HttpFields.Mutable mutableHttpFields = mutableFields();
|
||||
if (key instanceof String name) {
|
||||
List<String> oldValues = get(key);
|
||||
mutableHttpFields.remove(name);
|
||||
@@ -168,9 +165,7 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if (!(this.headers instanceof HttpFields.Mutable mutableHttpFields)) {
|
||||
throw new IllegalStateException(IMMUTABLE_HEADER_ERROR);
|
||||
}
|
||||
HttpFields.Mutable mutableHttpFields = mutableFields();
|
||||
mutableHttpFields.clear();
|
||||
}
|
||||
|
||||
@@ -199,6 +194,16 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
};
|
||||
}
|
||||
|
||||
private HttpFields.Mutable mutableFields() {
|
||||
if (this.headers instanceof HttpFields.Mutable mutableHttpFields) {
|
||||
return mutableHttpFields;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Immutable headers");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -242,9 +247,7 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
|
||||
@Override
|
||||
public List<String> setValue(List<String> value) {
|
||||
if (!(headers instanceof HttpFields.Mutable mutableHttpFields)) {
|
||||
throw new IllegalStateException(IMMUTABLE_HEADER_ERROR);
|
||||
}
|
||||
HttpFields.Mutable mutableHttpFields = mutableFields();
|
||||
List<String> previousValues = headers.getValuesList(this.key);
|
||||
mutableHttpFields.put(this.key, value);
|
||||
return previousValues;
|
||||
@@ -290,9 +293,7 @@ public final class JettyHeadersAdapter implements MultiValueMap<String, String>
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (!(headers instanceof HttpFields.Mutable mutableHttpFields)) {
|
||||
throw new IllegalStateException(IMMUTABLE_HEADER_ERROR);
|
||||
}
|
||||
HttpFields.Mutable mutableHttpFields = mutableFields();
|
||||
if (this.currentName == null) {
|
||||
throw new IllegalStateException("No current Header in iterator");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user