Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Eclipse Jetty core HTTP handler adapter #32097

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
6ff974d
Initial boiler plate implementation of JettyCore HttpHandler Adaptor
gregw Jan 15, 2024
db609a7
More implementation of the request side
gregw Jan 15, 2024
7a23fbf
Created JettyCoreHttpServer
gregw Jan 15, 2024
418d699
Created JettyCoreHttpServer
gregw Jan 15, 2024
930d420
WIP on tests
gregw Jan 16, 2024
eb38434
WIP on tests
gregw Jan 16, 2024
88e7467
Cleanup start/stop for jetty
gregw Jan 16, 2024
c5a327c
retainable bytebuffer!
gregw Jan 16, 2024
49abe72
Non blocking invocation type
gregw Jan 16, 2024
6ca453c
split out into separate files
gregw Jan 16, 2024
ea71c61
minor httpHeader optimizations
gregw Jan 18, 2024
dd72a14
since updated with expected release
gregw Jan 20, 2024
50e78d3
Initial implementation of the writeWith methods in JettyCoreServerHtt…
lachlan-roberts Jan 22, 2024
ab844d6
Implement the ZeroCopyHttpOutputMessage interface for JettyCoreServer…
lachlan-roberts Jan 22, 2024
4cf2b0c
Add temporary fix for cookies
lachlan-roberts Jan 22, 2024
77772cb
disable setting of the same site cookie attribute to fix test
lachlan-roberts Jan 22, 2024
8617ded
rearrange methods in JettyCoreServerHttpResponse
lachlan-roberts Jan 22, 2024
375e3b6
fix issues with cookies, committing response, and with ZeroCopyHttpOu…
lachlan-roberts Jan 23, 2024
0db46f2
cleanups
lachlan-roberts Jan 23, 2024
2081ae0
cleanups
lachlan-roberts Jan 23, 2024
855e268
removed TODO
gregw Jan 24, 2024
191853d
Reformatted code
gregw Jan 24, 2024
c8a7cc3
Fixed unset response status bug
gregw Jan 24, 2024
85bdb2b
Request header mutation support
gregw Jan 24, 2024
39f9ce8
Do not use canonical path
gregw Jan 24, 2024
c46382e
Track leaks (for now) and retain DataBuffer chunks
gregw Jan 25, 2024
72921c0
Fixes for Jetty Core WebSocket
lachlan-roberts Jan 30, 2024
05df5d1
Temporary fix for cookies in JettyCoreServerHttpResponse with TODOs
lachlan-roberts Jan 30, 2024
89dbcbf
Do not actually commit response in JettyCoreServerHttpResponse & fix …
lachlan-roberts Jan 30, 2024
654407e
Add JettyCore upgrade option in HandshakeWebSocketService
lachlan-roberts Jan 30, 2024
802419e
move getNativeRequest/Response to ServerHttpRequest/Response interfaces
lachlan-roberts Jan 30, 2024
4518138
Reformatted code
gregw Jan 31, 2024
67622d1
WIP on fixing leaks
gregw Jan 31, 2024
0530dd7
WIP on fixing leaks
gregw Jan 31, 2024
a5b7d21
WIP on fixing tests
gregw Jan 31, 2024
f25837c
Updated to jetty 12.0.6
gregw Feb 1, 2024
7cf3214
Move all response cookies to multiMap for possible mutation by spring
gregw Feb 1, 2024
c8c3734
Use an atomicReference to release the last used databuffer after onNe…
gregw Feb 1, 2024
74aa6cb
less allocations for releasing after onNext
gregw Feb 1, 2024
befdde4
release DataBuffer from the JettyCoreServerHttpResponse
lachlan-roberts Feb 1, 2024
ee59264
fixed style
gregw Feb 3, 2024
bbd2573
fix build with jetty 12.0.7-SNAPSHOT
olamy Feb 6, 2024
b1c778d
Updated to jetty 12.0.6
gregw Feb 1, 2024
5f1712d
Implement a JettyWebSocketSession based on demand.
lachlan-roberts Feb 6, 2024
700f8e6
Add a Jetty implementation of the spring WebSocketClient interface.
lachlan-roberts Feb 6, 2024
1c7935e
Prevent possible ReadPendingException from multiple demand.
lachlan-roberts Feb 6, 2024
bbd4e68
Update the JettyWebSocketHandlerAdapter as a Session.Listener
lachlan-roberts Feb 6, 2024
1f09a94
fixes for checkstyle
lachlan-roberts Feb 6, 2024
08e9177
Ensure DataBuffer is processed correctly for BINARY messages.
lachlan-roberts Feb 12, 2024
26cac28
Wait for close of Session before calling handlerCompletionSink
lachlan-roberts Feb 13, 2024
e2b452a
fix checkstyle error
lachlan-roberts Feb 13, 2024
b356adf
Merge remote-tracking branch 'webtide/main' into JettyCoreHttpHandler…
gregw Feb 20, 2024
a0f4fe2
Changes from review.
lachlan-roberts Feb 27, 2024
0f88d7f
Merge remote-tracking branch 'webtide/main' into JettyCoreHttpHandler…
gregw Mar 7, 2024
65cf738
updated to jetty 12.0.7
gregw Mar 7, 2024
6e25189
updated to jetty 12.0.7
gregw Mar 7, 2024
8f9d07c
Merge remote-tracking branch 'webtide/main' into JettyCoreHttpHandler…
gregw Mar 7, 2024
34429fe
updated to jetty 12.0.7
gregw Mar 7, 2024
5d5e46a
fixed checkstyle
gregw Mar 22, 2024
6187cdf
Merge branch 'main' into JettyCoreHttpHandlerAdapter
gregw Mar 22, 2024
d9202dc
Merge branch 'spring-projects:main' into JettyCoreHttpHandlerAdapter
gregw Apr 4, 2024
85505fe
Implement LifeCycle for JettyWebSocketClient
lachlan-roberts Apr 9, 2024
cd9063b
additional changes from review
lachlan-roberts Apr 9, 2024
e9cb9a9
Merge pull request #2 from webtide/JettyCoreHttpHandlerAdapter-WebSocket
lachlan-roberts Apr 16, 2024
d6986ee
Use abstract request/response classes
gregw May 29, 2024
19215d4
Suppress NullAway warnings for Jetty WebSocket classes
lachlan-roberts May 29, 2024
364183c
fix other checkstyle warnings
lachlan-roberts May 29, 2024
a76465a
backed out abstract response change
gregw May 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ configure(allprojects) { project ->
apply plugin: "org.springframework.build.localdev"
group = "org.springframework"
repositories {
mavenLocal()
mavenCentral()
maven {
url "https://repo.spring.io/milestone"
Expand Down
1 change: 1 addition & 0 deletions framework-api/framework-api.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ description = "Spring Framework API Docs"
apply from: "${rootDir}/gradle/publications.gradle"

repositories {
mavenLocal()
gregw marked this conversation as resolved.
Show resolved Hide resolved
maven {
url "https://repo.spring.io/release"
}
Expand Down
10 changes: 7 additions & 3 deletions framework-platform/framework-platform.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ javaPlatform {
allowDependencies()
}

repositories {
mavenLocal()
}

dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.15.3"))
api(platform("io.micrometer:micrometer-bom:1.12.2"))
Expand All @@ -14,10 +18,10 @@ dependencies {
api(platform("io.projectreactor:reactor-bom:2023.0.2"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.17"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.apache.logging.log4j:log4j-bom:2.22.1"))
api(platform("org.assertj:assertj-bom:3.25.2"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.5"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.5"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.7-SNAPSHOT"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.7-SNAPSHOT"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0"))
api(platform("org.junit:junit-bom:5.10.2"))
Expand Down
2 changes: 2 additions & 0 deletions spring-jcl/spring-jcl.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ description = "Spring Commons Logging Bridge"
dependencies {
optional("org.apache.logging.log4j:log4j-api")
optional("org.slf4j:slf4j-api")
optional("biz.aQute.bnd:biz.aQute.bnd.annotation:6.3.1")
optional("org.osgi:osgi.annotation:8.1.0")
}
1 change: 1 addition & 0 deletions spring-web/spring-web.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ dependencies {
because("needed by Netty's SelfSignedCertificate on JDK 15+")
}
testFixturesImplementation("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server")
testFixturesImplementation("org.eclipse.jetty.websocket:jetty-websocket-jetty-server")
testImplementation(project(":spring-core-test"))
testImplementation(testFixtures(project(":spring-beans")))
testImplementation(testFixtures(project(":spring-context")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
*/
public abstract class AbstractServerHttpRequest implements ServerHttpRequest {

private static final Pattern QUERY_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?");
static final Pattern QUERY_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?");


private final URI uri;
Expand Down Expand Up @@ -203,13 +203,6 @@ public SslInfo getSslInfo() {
@Nullable
protected abstract SslInfo initSslInfo();

/**
* Return the underlying server response.
* <p><strong>Note:</strong> This is exposed mainly for internal framework
* use such as WebSocket upgrades in the spring-webflux module.
*/
public abstract <T> T getNativeRequest();

/**
* For internal use in logging at the HTTP adapter layer.
* @since 5.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,6 @@ public void addCookie(ResponseCookie cookie) {
}
}

/**
* Return the underlying server response.
* <p><strong>Note:</strong> This is exposed mainly for internal framework
* use such as WebSocket upgrades in the spring-webflux module.
*/
public abstract <T> T getNativeResponse();


@Override
public void beforeCommit(Supplier<? extends Mono<Void>> action) {
this.commitActions.add(action);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Consumer;

import reactor.core.publisher.Flux;
Expand Down Expand Up @@ -67,14 +68,29 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {


public DefaultServerHttpRequestBuilder(ServerHttpRequest original) {
Assert.notNull(original, "ServerHttpRequest is required");

this.uri = original.getURI();
this.headers = HttpHeaders.writableHttpHeaders(original.getHeaders());
this.httpMethod = original.getMethod();
this.contextPath = original.getPath().contextPath().value();
this.remoteAddress = original.getRemoteAddress();
this.body = original.getBody();
this(original.getURI(),
HttpHeaders.writableHttpHeaders(original.getHeaders()),
original.getMethod(),original.getPath().contextPath().value(),
original.getRemoteAddress(),
original.getBody(),
Objects.requireNonNull(original, "ServerHttpRequest is required"));
}


public DefaultServerHttpRequestBuilder(
URI uri,
HttpHeaders httpHeaders,
HttpMethod method,
String contextPath,
@Nullable InetSocketAddress remoteAddress,
Flux<DataBuffer> body,
ServerHttpRequest original) {
this.uri = uri;
this.headers = httpHeaders;
this.httpMethod = method;
this.contextPath = contextPath;
this.remoteAddress = remoteAddress;
this.body = body;
this.originalRequest = original;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.http.server.reactive;

import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;

/**
* Adapt {@link HttpHandler} to the Jetty {@link org.eclipse.jetty.server.Handler} abstraction.
*
* @author Greg Wilkins
* @author Lachlan Roberts
* @since 6.2
*/
public class JettyCoreHttpHandlerAdapter extends Handler.Abstract.NonBlocking {

private final HttpHandler httpHandler;

private final DataBufferFactory dataBufferFactory;

public JettyCoreHttpHandlerAdapter(HttpHandler httpHandler) {
this.httpHandler = httpHandler;

// Currently we do not make a DataBufferFactory over the servers ByteBufferPool,
// because we mainly use wrap and there should be few allocation done by the factory.
// But it could be possible to use the servers buffer pool for allocations and to
// create PooledDataBuffers
this.dataBufferFactory = new DefaultDataBufferFactory();
}

@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {
this.httpHandler.handle(new JettyCoreServerHttpRequest(this.dataBufferFactory, request), new JettyCoreServerHttpResponse(response))
.subscribe(new Subscriber<>() {
@Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE);
}

@Override
public void onNext(Void unused) {
// we can ignore the void as we only seek onError or onComplete
}

@Override
public void onError(Throwable t) {
callback.failed(t);
}

@Override
public void onComplete() {
callback.succeeded();
}
});
return true;
}

}