Skip to content

Commit 0a60c62

Browse files
gregwlachlan-robertspoutsma
authored andcommitted
Implement Eclipse Jetty core HTTP handler adapter
This provides an implementation of an HTTP Handler Adapter that is coded directly to the Eclipse Jetty core API, bypassing any servlet implementation. This includes a Jetty implementation of the spring `WebSocketClient` interface, `JettyWebSocketClient`, using an explicit dependency to the jetty-websocket-api. Closes gh-32097 Co-authored-by: Lachlan Roberts <[email protected]> Co-authored-by: Arjen Poutsma <[email protected]>
1 parent b7ec028 commit 0a60c62

File tree

33 files changed

+1692
-517
lines changed

33 files changed

+1692
-517
lines changed

framework-platform/framework-platform.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ dependencies {
1616
api(platform("org.apache.groovy:groovy-bom:4.0.21"))
1717
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
1818
api(platform("org.assertj:assertj-bom:3.26.0"))
19-
api(platform("org.eclipse.jetty:jetty-bom:12.0.10"))
20-
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.10"))
19+
api(platform("org.eclipse.jetty:jetty-bom:12.0.11"))
20+
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.11"))
2121
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3"))
2222
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0"))
2323
api(platform("org.junit:junit-bom:5.10.3"))

spring-core/spring-core.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ dependencies {
8181
optional("io.smallrye.reactive:mutiny")
8282
optional("net.sf.jopt-simple:jopt-simple")
8383
optional("org.aspectj:aspectjweaver")
84+
optional("org.eclipse.jetty:jetty-io")
8485
optional("org.jetbrains.kotlin:kotlin-reflect")
8586
optional("org.jetbrains.kotlin:kotlin-stdlib")
8687
optional("org.jetbrains.kotlinx:kotlinx-coroutines-core")

spring-core/src/main/java/org/springframework/core/io/buffer/DefaultDataBuffer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ public DefaultDataBuffer slice(int index, int length) {
355355
}
356356

357357
@Override
358-
public DataBuffer split(int index) {
358+
public DefaultDataBuffer split(int index) {
359359
checkIndex(index);
360360

361361
ByteBuffer split = this.byteBuffer.duplicate().clear()
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.io.buffer;
18+
19+
import java.nio.ByteBuffer;
20+
import java.nio.charset.Charset;
21+
import java.util.concurrent.atomic.AtomicInteger;
22+
import java.util.function.IntPredicate;
23+
24+
import org.eclipse.jetty.io.Content;
25+
26+
import org.springframework.lang.Nullable;
27+
import org.springframework.util.Assert;
28+
29+
/**
30+
* Implementation of the {@code DataBuffer} interface that can wrap a Jetty
31+
* {@link Content.Chunk}. Typically constructed with {@link JettyDataBufferFactory}.
32+
*
33+
* @author Greg Wilkins
34+
* @author Lachlan Roberts
35+
* @author Arjen Poutsma
36+
* @since 6.2
37+
*/
38+
public final class JettyDataBuffer implements PooledDataBuffer {
39+
40+
private final DefaultDataBuffer delegate;
41+
42+
@Nullable
43+
private final Content.Chunk chunk;
44+
45+
private final JettyDataBufferFactory bufferFactory;
46+
47+
private final AtomicInteger refCount = new AtomicInteger(1);
48+
49+
50+
JettyDataBuffer(JettyDataBufferFactory bufferFactory, DefaultDataBuffer delegate, Content.Chunk chunk) {
51+
Assert.notNull(bufferFactory, "BufferFactory must not be null");
52+
Assert.notNull(delegate, "Delegate must not be null");
53+
Assert.notNull(chunk, "Chunk must not be null");
54+
55+
this.bufferFactory = bufferFactory;
56+
this.delegate = delegate;
57+
this.chunk = chunk;
58+
this.chunk.retain();
59+
}
60+
61+
JettyDataBuffer(JettyDataBufferFactory bufferFactory, DefaultDataBuffer delegate) {
62+
Assert.notNull(bufferFactory, "BufferFactory must not be null");
63+
Assert.notNull(delegate, "Delegate must not be null");
64+
65+
this.bufferFactory = bufferFactory;
66+
this.delegate = delegate;
67+
this.chunk = null;
68+
}
69+
70+
@Override
71+
public boolean isAllocated() {
72+
return this.refCount.get() > 0;
73+
}
74+
75+
@Override
76+
public PooledDataBuffer retain() {
77+
int result = this.refCount.updateAndGet(c -> {
78+
if (c != 0) {
79+
return c + 1;
80+
}
81+
else {
82+
return 0;
83+
}
84+
});
85+
if (result != 0 && this.chunk != null) {
86+
this.chunk.retain();
87+
}
88+
return this;
89+
}
90+
91+
@Override
92+
public PooledDataBuffer touch(Object hint) {
93+
return this;
94+
}
95+
96+
@Override
97+
public boolean release() {
98+
int result = this.refCount.updateAndGet(c -> {
99+
if (c != 0) {
100+
return c - 1;
101+
}
102+
else {
103+
throw new IllegalStateException("JettyDataBuffer already released: " + this);
104+
}
105+
});
106+
if (this.chunk != null) {
107+
return this.chunk.release();
108+
}
109+
else {
110+
return result == 0;
111+
}
112+
}
113+
114+
@Override
115+
public DataBufferFactory factory() {
116+
return this.bufferFactory;
117+
}
118+
119+
// delegation
120+
121+
@Override
122+
public int indexOf(IntPredicate predicate, int fromIndex) {
123+
return this.delegate.indexOf(predicate, fromIndex);
124+
}
125+
126+
@Override
127+
public int lastIndexOf(IntPredicate predicate, int fromIndex) {
128+
return this.delegate.lastIndexOf(predicate, fromIndex);
129+
}
130+
131+
@Override
132+
public int readableByteCount() {
133+
return this.delegate.readableByteCount();
134+
}
135+
136+
@Override
137+
public int writableByteCount() {
138+
return this.delegate.writableByteCount();
139+
}
140+
141+
@Override
142+
public int capacity() {
143+
return this.delegate.capacity();
144+
}
145+
146+
@Override
147+
@Deprecated
148+
public DataBuffer capacity(int capacity) {
149+
this.delegate.capacity(capacity);
150+
return this;
151+
}
152+
153+
@Override
154+
public DataBuffer ensureWritable(int capacity) {
155+
this.delegate.ensureWritable(capacity);
156+
return this;
157+
}
158+
159+
@Override
160+
public int readPosition() {
161+
return this.delegate.readPosition();
162+
}
163+
164+
@Override
165+
public DataBuffer readPosition(int readPosition) {
166+
this.delegate.readPosition(readPosition);
167+
return this;
168+
}
169+
170+
@Override
171+
public int writePosition() {
172+
return this.delegate.writePosition();
173+
}
174+
175+
@Override
176+
public DataBuffer writePosition(int writePosition) {
177+
this.delegate.writePosition(writePosition);
178+
return this;
179+
}
180+
181+
@Override
182+
public byte getByte(int index) {
183+
return this.delegate.getByte(index);
184+
}
185+
186+
@Override
187+
public byte read() {
188+
return this.delegate.read();
189+
}
190+
191+
@Override
192+
public DataBuffer read(byte[] destination) {
193+
this.delegate.read(destination);
194+
return this;
195+
}
196+
197+
@Override
198+
public DataBuffer read(byte[] destination, int offset, int length) {
199+
this.delegate.read(destination, offset, length);
200+
return this;
201+
}
202+
203+
@Override
204+
public DataBuffer write(byte b) {
205+
this.delegate.write(b);
206+
return this;
207+
}
208+
209+
@Override
210+
public DataBuffer write(byte[] source) {
211+
this.delegate.write(source);
212+
return this;
213+
}
214+
215+
@Override
216+
public DataBuffer write(byte[] source, int offset, int length) {
217+
this.delegate.write(source, offset, length);
218+
return this;
219+
}
220+
221+
@Override
222+
public DataBuffer write(DataBuffer... buffers) {
223+
this.delegate.write(buffers);
224+
return this;
225+
}
226+
227+
@Override
228+
public DataBuffer write(ByteBuffer... buffers) {
229+
this.delegate.write(buffers);
230+
return this;
231+
}
232+
233+
@Override
234+
@Deprecated
235+
public DataBuffer slice(int index, int length) {
236+
DefaultDataBuffer delegateSlice = this.delegate.slice(index, length);
237+
if (this.chunk != null) {
238+
this.chunk.retain();
239+
return new JettyDataBuffer(this.bufferFactory, delegateSlice, this.chunk);
240+
}
241+
else {
242+
return new JettyDataBuffer(this.bufferFactory, delegateSlice);
243+
}
244+
}
245+
246+
@Override
247+
public DataBuffer split(int index) {
248+
DefaultDataBuffer delegateSplit = this.delegate.split(index);
249+
if (this.chunk != null) {
250+
this.chunk.retain();
251+
return new JettyDataBuffer(this.bufferFactory, delegateSplit, this.chunk);
252+
}
253+
else {
254+
return new JettyDataBuffer(this.bufferFactory, delegateSplit);
255+
}
256+
}
257+
258+
@Override
259+
@Deprecated
260+
public ByteBuffer asByteBuffer() {
261+
return this.delegate.asByteBuffer();
262+
}
263+
264+
@Override
265+
@Deprecated
266+
public ByteBuffer asByteBuffer(int index, int length) {
267+
return this.delegate.asByteBuffer(index, length);
268+
}
269+
270+
@Override
271+
@Deprecated
272+
public ByteBuffer toByteBuffer(int index, int length) {
273+
return this.delegate.toByteBuffer(index, length);
274+
}
275+
276+
@Override
277+
public void toByteBuffer(int srcPos, ByteBuffer dest, int destPos, int length) {
278+
this.delegate.toByteBuffer(srcPos, dest, destPos, length);
279+
}
280+
281+
@Override
282+
public ByteBufferIterator readableByteBuffers() {
283+
ByteBufferIterator delegateIterator = this.delegate.readableByteBuffers();
284+
if (this.chunk != null) {
285+
return new JettyByteBufferIterator(delegateIterator, this.chunk);
286+
}
287+
else {
288+
return delegateIterator;
289+
}
290+
}
291+
292+
@Override
293+
public ByteBufferIterator writableByteBuffers() {
294+
ByteBufferIterator delegateIterator = this.delegate.writableByteBuffers();
295+
if (this.chunk != null) {
296+
return new JettyByteBufferIterator(delegateIterator, this.chunk);
297+
}
298+
else {
299+
return delegateIterator;
300+
}
301+
}
302+
303+
@Override
304+
public String toString(int index, int length, Charset charset) {
305+
return this.delegate.toString(index, length, charset);
306+
}
307+
308+
@Override
309+
public int hashCode() {
310+
return this.delegate.hashCode();
311+
}
312+
313+
@Override
314+
public boolean equals(Object o) {
315+
return this == o || (o instanceof JettyDataBuffer other &&
316+
this.delegate.equals(other.delegate));
317+
}
318+
319+
@Override
320+
public String toString() {
321+
return String.format("JettyDataBuffer (r: %d, w: %d, c: %d)",
322+
readPosition(), writePosition(), capacity());
323+
}
324+
325+
private static final class JettyByteBufferIterator implements ByteBufferIterator {
326+
327+
private final ByteBufferIterator delegate;
328+
329+
private final Content.Chunk chunk;
330+
331+
332+
public JettyByteBufferIterator(ByteBufferIterator delegate, Content.Chunk chunk) {
333+
Assert.notNull(delegate, "Delegate must not be null");
334+
Assert.notNull(chunk, "Chunk must not be null");
335+
336+
this.delegate = delegate;
337+
this.chunk = chunk;
338+
this.chunk.retain();
339+
}
340+
341+
342+
@Override
343+
public void close() {
344+
this.delegate.close();
345+
this.chunk.release();
346+
}
347+
348+
@Override
349+
public boolean hasNext() {
350+
return this.delegate.hasNext();
351+
}
352+
353+
@Override
354+
public ByteBuffer next() {
355+
return this.delegate.next();
356+
}
357+
}
358+
359+
}

0 commit comments

Comments
 (0)