1818
1919import java .nio .ByteBuffer ;
2020import java .nio .charset .StandardCharsets ;
21- import java .util .concurrent .atomic .AtomicLong ;
21+ import java .util .concurrent .locks .Lock ;
22+ import java .util .concurrent .locks .ReentrantLock ;
2223
24+ import org .eclipse .jetty .util .BufferUtil ;
2325import org .eclipse .jetty .websocket .api .Callback ;
2426import org .eclipse .jetty .websocket .api .Session ;
2527import org .reactivestreams .Publisher ;
4850public class JettyWebSocketSession extends AbstractWebSocketSession <Session > {
4951
5052private final Flux <WebSocketMessage > flux ;
51- private final AtomicLong requested = new AtomicLong (0 );
52-
5353private final Sinks .One <CloseStatus > closeStatusSink = Sinks .one ();
54- @ Nullable
54+ private final Lock lock = new ReentrantLock ();
55+ private long requested = 0 ;
56+ private boolean awaitingDemand = false ;
57+
58+ @ SuppressWarnings ("NotNullFieldNotInitialized" )
5559private FluxSink <WebSocketMessage > sink ;
5660
5761@ Nullable
@@ -70,15 +74,49 @@ public JettyWebSocketSession(Session session, HandshakeInfo info, DataBufferFact
7074this .sink = emitter ;
7175emitter .onRequest (n ->
7276{
73- requested .addAndGet (n );
74- tryDemand ();
77+ boolean demand = false ;
78+ lock .lock ();
79+ try
80+ {
81+ requested += n ;
82+ if (!awaitingDemand && requested > 0 ) {
83+ requested --;
84+ awaitingDemand = true ;
85+ demand = true ;
86+ }
87+ }
88+ finally {
89+ lock .unlock ();
90+ }
91+
92+ if (demand )
93+ getDelegate ().demand ();
7594});
7695});
7796}
7897
79- void handleMessage (WebSocketMessage . Type type , WebSocketMessage message ) {
98+ void handleMessage (WebSocketMessage message ) {
8099this .sink .next (message );
81- tryDemand ();
100+
101+ boolean demand = false ;
102+ lock .lock ();
103+ try
104+ {
105+ if (!awaitingDemand )
106+ throw new IllegalStateException ();
107+ awaitingDemand = false ;
108+ if (requested > 0 ) {
109+ requested --;
110+ awaitingDemand = true ;
111+ demand = true ;
112+ }
113+ }
114+ finally {
115+ lock .unlock ();
116+ }
117+
118+ if (demand )
119+ getDelegate ().demand ();
82120}
83121
84122void handleError (Throwable ex ) {
@@ -127,23 +165,6 @@ public Flux<WebSocketMessage> receive() {
127165return flux ;
128166}
129167
130- private void tryDemand ()
131- {
132- while (true )
133- {
134- long r = requested .get ();
135- if (r == 0 )
136- return ;
137-
138- // TODO: protect against readpending from multiple demand.
139- if (requested .compareAndSet (r , r - 1 ))
140- {
141- getDelegate ().demand ();
142- return ;
143- }
144- }
145- }
146-
147168@ Override
148169public Mono <Void > send (Publisher <WebSocketMessage > messages ) {
149170return Flux .from (messages )
@@ -162,17 +183,31 @@ protected Mono<Void> sendMessage(WebSocketMessage message) {
162183session .sendText (text , completable );
163184}
164185else {
165- // TODO: Ping and Pong message should combine payload into single buffer?
166- try (DataBuffer .ByteBufferIterator iterator = dataBuffer .readableByteBuffers ()) {
167- while (iterator .hasNext ()) {
168- ByteBuffer byteBuffer = iterator .next ();
169- switch (message .getType ()) {
170- case BINARY -> session .sendBinary (byteBuffer , completable );
171- case PING -> session .sendPing (byteBuffer , completable );
172- case PONG -> session .sendPong (byteBuffer , completable );
173- default -> throw new IllegalArgumentException ("Unexpected message type: " + message .getType ());
186+ switch (message .getType ()) {
187+ case BINARY ->
188+ {
189+ try (DataBuffer .ByteBufferIterator iterator = dataBuffer .readableByteBuffers ()) {
190+ while (iterator .hasNext ()) {
191+ ByteBuffer byteBuffer = iterator .next ();
192+ session .sendBinary (byteBuffer , completable );
193+ }
174194}
175195}
196+ case PING ->
197+ {
198+ // Maximum size of Control frame payload is 125, per RFC 6455.
199+ ByteBuffer buffer = BufferUtil .allocate (125 );
200+ dataBuffer .toByteBuffer (buffer );
201+ session .sendPing (buffer , completable );
202+ }
203+ case PONG ->
204+ {
205+ // Maximum size of Control frame payload is 125, per RFC 6455.
206+ ByteBuffer buffer = BufferUtil .allocate (125 );
207+ dataBuffer .toByteBuffer (buffer );
208+ session .sendPong (buffer , completable );
209+ }
210+ default -> throw new IllegalArgumentException ("Unexpected message type: " + message .getType ());
176211}
177212}
178213return Mono .fromFuture (completable );
0 commit comments