2013-03-20 10:04:17 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2013 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you 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:
|
|
|
|
*
|
|
|
|
* http://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 io.netty.handler.codec.http.websocketx;
|
|
|
|
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
Revamp the core API to reduce memory footprint and consumption
The API changes made so far turned out to increase the memory footprint
and consumption while our intention was actually decreasing them.
Memory consumption issue:
When there are many connections which does not exchange data frequently,
the old Netty 4 API spent a lot more memory than 3 because it always
allocates per-handler buffer for each connection unless otherwise
explicitly stated by a user. In a usual real world load, a client
doesn't always send requests without pausing, so the idea of having a
buffer whose life cycle if bound to the life cycle of a connection
didn't work as expected.
Memory footprint issue:
The old Netty 4 API decreased overall memory footprint by a great deal
in many cases. It was mainly because the old Netty 4 API did not
allocate a new buffer and event object for each read. Instead, it
created a new buffer for each handler in a pipeline. This works pretty
well as long as the number of handlers in a pipeline is only a few.
However, for a highly modular application with many handlers which
handles connections which lasts for relatively short period, it actually
makes the memory footprint issue much worse.
Changes:
All in all, this is about retaining all the good changes we made in 4 so
far such as better thread model and going back to the way how we dealt
with message events in 3.
To fix the memory consumption/footprint issue mentioned above, we made a
hard decision to break the backward compatibility again with the
following changes:
- Remove MessageBuf
- Merge Buf into ByteBuf
- Merge ChannelInboundByte/MessageHandler and ChannelStateHandler into ChannelInboundHandler
- Similar changes were made to the adapter classes
- Merge ChannelOutboundByte/MessageHandler and ChannelOperationHandler into ChannelOutboundHandler
- Similar changes were made to the adapter classes
- Introduce MessageList which is similar to `MessageEvent` in Netty 3
- Replace inboundBufferUpdated(ctx) with messageReceived(ctx, MessageList)
- Replace flush(ctx, promise) with write(ctx, MessageList, promise)
- Remove ByteToByteEncoder/Decoder/Codec
- Replaced by MessageToByteEncoder<ByteBuf>, ByteToMessageDecoder<ByteBuf>, and ByteMessageCodec<ByteBuf>
- Merge EmbeddedByteChannel and EmbeddedMessageChannel into EmbeddedChannel
- Add SimpleChannelInboundHandler which is sometimes more useful than
ChannelInboundHandlerAdapter
- Bring back Channel.isWritable() from Netty 3
- Add ChannelInboundHandler.channelWritabilityChanges() event
- Add RecvByteBufAllocator configuration property
- Similar to ReceiveBufferSizePredictor in Netty 3
- Some existing configuration properties such as
DatagramChannelConfig.receivePacketSize is gone now.
- Remove suspend/resumeIntermediaryDeallocation() in ByteBuf
This change would have been impossible without @normanmaurer's help. He
fixed, ported, and improved many parts of the changes.
2013-05-28 13:40:19 +02:00
|
|
|
import io.netty.channel.ChannelInboundHandler;
|
2013-03-20 10:04:17 +01:00
|
|
|
import io.netty.channel.ChannelPipeline;
|
|
|
|
import io.netty.handler.codec.http.HttpHeaders;
|
|
|
|
|
|
|
|
import java.net.URI;
|
Remove MessageList from public API and change ChannelInbound/OutboundHandler accordingly
I must admit MesageList was pain in the ass. Instead of forcing a
handler always loop over the list of messages, this commit splits
messageReceived(ctx, list) into two event handlers:
- messageReceived(ctx, msg)
- mmessageReceivedLast(ctx)
When Netty reads one or more messages, messageReceived(ctx, msg) event
is triggered for each message. Once the current read operation is
finished, messageReceivedLast() is triggered to tell the handler that
the last messageReceived() was the last message in the current batch.
Similarly, for outbound, write(ctx, list) has been split into two:
- write(ctx, msg)
- flush(ctx, promise)
Instead of writing a list of message with a promise, a user is now
supposed to call write(msg) multiple times and then call flush() to
actually flush the buffered messages.
Please note that write() doesn't have a promise with it. You must call
flush() to get notified on completion. (or you can use writeAndFlush())
Other changes:
- Because MessageList is completely hidden, codec framework uses
List<Object> instead of MessageList as an output parameter.
2013-07-08 12:03:40 +02:00
|
|
|
import java.util.List;
|
2013-03-20 10:04:17 +01:00
|
|
|
|
2019-05-22 12:37:28 +02:00
|
|
|
import static io.netty.util.internal.ObjectUtil.*;
|
|
|
|
|
2013-03-20 10:04:17 +01:00
|
|
|
/**
|
|
|
|
* This handler does all the heavy lifting for you to run a websocket client.
|
|
|
|
*
|
|
|
|
* It takes care of websocket handshaking as well as processing of Ping, Pong frames. Text and Binary
|
|
|
|
* data frames are passed to the next handler in the pipeline (implemented by you) for processing.
|
|
|
|
* Also the close frame is passed to the next handler as you may want inspect it before close the connection if
|
|
|
|
* the {@code handleCloseFrames} is {@code false}, default is {@code true}.
|
|
|
|
*
|
|
|
|
* This implementation will establish the websocket connection once the connection to the remote server was complete.
|
2013-03-28 06:57:04 +01:00
|
|
|
*
|
|
|
|
* To know once a handshake was done you can intercept the
|
Revamp the core API to reduce memory footprint and consumption
The API changes made so far turned out to increase the memory footprint
and consumption while our intention was actually decreasing them.
Memory consumption issue:
When there are many connections which does not exchange data frequently,
the old Netty 4 API spent a lot more memory than 3 because it always
allocates per-handler buffer for each connection unless otherwise
explicitly stated by a user. In a usual real world load, a client
doesn't always send requests without pausing, so the idea of having a
buffer whose life cycle if bound to the life cycle of a connection
didn't work as expected.
Memory footprint issue:
The old Netty 4 API decreased overall memory footprint by a great deal
in many cases. It was mainly because the old Netty 4 API did not
allocate a new buffer and event object for each read. Instead, it
created a new buffer for each handler in a pipeline. This works pretty
well as long as the number of handlers in a pipeline is only a few.
However, for a highly modular application with many handlers which
handles connections which lasts for relatively short period, it actually
makes the memory footprint issue much worse.
Changes:
All in all, this is about retaining all the good changes we made in 4 so
far such as better thread model and going back to the way how we dealt
with message events in 3.
To fix the memory consumption/footprint issue mentioned above, we made a
hard decision to break the backward compatibility again with the
following changes:
- Remove MessageBuf
- Merge Buf into ByteBuf
- Merge ChannelInboundByte/MessageHandler and ChannelStateHandler into ChannelInboundHandler
- Similar changes were made to the adapter classes
- Merge ChannelOutboundByte/MessageHandler and ChannelOperationHandler into ChannelOutboundHandler
- Similar changes were made to the adapter classes
- Introduce MessageList which is similar to `MessageEvent` in Netty 3
- Replace inboundBufferUpdated(ctx) with messageReceived(ctx, MessageList)
- Replace flush(ctx, promise) with write(ctx, MessageList, promise)
- Remove ByteToByteEncoder/Decoder/Codec
- Replaced by MessageToByteEncoder<ByteBuf>, ByteToMessageDecoder<ByteBuf>, and ByteMessageCodec<ByteBuf>
- Merge EmbeddedByteChannel and EmbeddedMessageChannel into EmbeddedChannel
- Add SimpleChannelInboundHandler which is sometimes more useful than
ChannelInboundHandlerAdapter
- Bring back Channel.isWritable() from Netty 3
- Add ChannelInboundHandler.channelWritabilityChanges() event
- Add RecvByteBufAllocator configuration property
- Similar to ReceiveBufferSizePredictor in Netty 3
- Some existing configuration properties such as
DatagramChannelConfig.receivePacketSize is gone now.
- Remove suspend/resumeIntermediaryDeallocation() in ByteBuf
This change would have been impossible without @normanmaurer's help. He
fixed, ported, and improved many parts of the changes.
2013-05-28 13:40:19 +02:00
|
|
|
* {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} and check if the event was of type
|
2013-03-28 06:57:04 +01:00
|
|
|
* {@link ClientHandshakeStateEvent#HANDSHAKE_ISSUED} or {@link ClientHandshakeStateEvent#HANDSHAKE_COMPLETE}.
|
2013-03-20 10:04:17 +01:00
|
|
|
*/
|
|
|
|
public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler {
|
2019-05-22 12:37:28 +02:00
|
|
|
private static final long DEFAULT_HANDSHAKE_TIMEOUT_MS = 10000L;
|
2013-03-20 10:04:17 +01:00
|
|
|
|
|
|
|
private final WebSocketClientHandshaker handshaker;
|
|
|
|
private final boolean handleCloseFrames;
|
2019-05-22 12:37:28 +02:00
|
|
|
private final long handshakeTimeoutMillis;
|
2013-03-20 10:04:17 +01:00
|
|
|
|
2014-09-26 21:56:44 +02:00
|
|
|
/**
|
|
|
|
* Returns the used handshaker
|
|
|
|
*/
|
2019-01-25 11:58:42 +01:00
|
|
|
public WebSocketClientHandshaker handshaker() {
|
|
|
|
return handshaker;
|
|
|
|
}
|
2014-09-26 21:56:44 +02:00
|
|
|
|
2013-03-28 06:57:04 +01:00
|
|
|
/**
|
|
|
|
* Events that are fired to notify about handshake status
|
|
|
|
*/
|
|
|
|
public enum ClientHandshakeStateEvent {
|
2019-05-22 12:37:28 +02:00
|
|
|
/**
|
|
|
|
* The Handshake was timed out
|
|
|
|
*/
|
|
|
|
HANDSHAKE_TIMEOUT,
|
|
|
|
|
2013-03-28 06:57:04 +01:00
|
|
|
/**
|
|
|
|
* The Handshake was started but the server did not response yet to the request
|
|
|
|
*/
|
|
|
|
HANDSHAKE_ISSUED,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The Handshake was complete succesful and so the channel was upgraded to websockets
|
|
|
|
*/
|
|
|
|
HANDSHAKE_COMPLETE
|
|
|
|
}
|
|
|
|
|
2014-10-22 21:59:45 +02:00
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param webSocketURL
|
|
|
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
|
|
|
* sent to this URL.
|
|
|
|
* @param version
|
|
|
|
* Version of web socket specification to use to connect to the server
|
|
|
|
* @param subprotocol
|
|
|
|
* Sub protocol request sent to the server.
|
|
|
|
* @param customHeaders
|
|
|
|
* Map of custom headers to add to the client request
|
|
|
|
* @param maxFramePayloadLength
|
|
|
|
* Maximum length of a frame's payload
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
* @param performMasking
|
|
|
|
* Whether to mask all written websocket frames. This must be set to true in order to be fully compatible
|
|
|
|
* with the websocket specifications. Client applications that communicate with a non-standard server
|
|
|
|
* which doesn't require masking might set this to false to achieve a higher performance.
|
|
|
|
* @param allowMaskMismatch
|
2017-02-15 20:03:59 +01:00
|
|
|
* When set to true, frames which are not masked properly according to the standard will still be
|
|
|
|
* accepted.
|
2014-10-22 21:59:45 +02:00
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
|
|
|
boolean allowExtensions, HttpHeaders customHeaders,
|
|
|
|
int maxFramePayloadLength, boolean handleCloseFrames,
|
|
|
|
boolean performMasking, boolean allowMaskMismatch) {
|
2019-05-22 12:37:28 +02:00
|
|
|
this(webSocketURL, version, subprotocol, allowExtensions, customHeaders,
|
|
|
|
maxFramePayloadLength, handleCloseFrames, performMasking, allowMaskMismatch, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param webSocketURL
|
|
|
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
|
|
|
* sent to this URL.
|
|
|
|
* @param version
|
|
|
|
* Version of web socket specification to use to connect to the server
|
|
|
|
* @param subprotocol
|
|
|
|
* Sub protocol request sent to the server.
|
|
|
|
* @param customHeaders
|
|
|
|
* Map of custom headers to add to the client request
|
|
|
|
* @param maxFramePayloadLength
|
|
|
|
* Maximum length of a frame's payload
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
* @param performMasking
|
|
|
|
* Whether to mask all written websocket frames. This must be set to true in order to be fully compatible
|
|
|
|
* with the websocket specifications. Client applications that communicate with a non-standard server
|
|
|
|
* which doesn't require masking might set this to false to achieve a higher performance.
|
|
|
|
* @param allowMaskMismatch
|
|
|
|
* When set to true, frames which are not masked properly according to the standard will still be
|
|
|
|
* accepted.
|
|
|
|
* @param handshakeTimeoutMillis
|
|
|
|
* Handshake timeout in mills, when handshake timeout, will trigger user
|
|
|
|
* event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT}
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
|
|
|
boolean allowExtensions, HttpHeaders customHeaders,
|
|
|
|
int maxFramePayloadLength, boolean handleCloseFrames, boolean performMasking,
|
|
|
|
boolean allowMaskMismatch, long handshakeTimeoutMillis) {
|
2014-10-22 21:59:45 +02:00
|
|
|
this(WebSocketClientHandshakerFactory.newHandshaker(webSocketURL, version, subprotocol,
|
|
|
|
allowExtensions, customHeaders, maxFramePayloadLength,
|
2019-05-22 12:37:28 +02:00
|
|
|
performMasking, allowMaskMismatch),
|
|
|
|
handleCloseFrames, handshakeTimeoutMillis);
|
2014-10-22 21:59:45 +02:00
|
|
|
}
|
|
|
|
|
2013-03-20 10:04:17 +01:00
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param webSocketURL
|
|
|
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
|
|
|
* sent to this URL.
|
|
|
|
* @param version
|
|
|
|
* Version of web socket specification to use to connect to the server
|
|
|
|
* @param subprotocol
|
|
|
|
* Sub protocol request sent to the server.
|
|
|
|
* @param customHeaders
|
|
|
|
* Map of custom headers to add to the client request
|
|
|
|
* @param maxFramePayloadLength
|
|
|
|
* Maximum length of a frame's payload
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
|
|
|
boolean allowExtensions, HttpHeaders customHeaders,
|
|
|
|
int maxFramePayloadLength, boolean handleCloseFrames) {
|
2014-10-22 21:59:45 +02:00
|
|
|
this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength,
|
2019-05-22 12:37:28 +02:00
|
|
|
handleCloseFrames, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param webSocketURL
|
|
|
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
|
|
|
* sent to this URL.
|
|
|
|
* @param version
|
|
|
|
* Version of web socket specification to use to connect to the server
|
|
|
|
* @param subprotocol
|
|
|
|
* Sub protocol request sent to the server.
|
|
|
|
* @param customHeaders
|
|
|
|
* Map of custom headers to add to the client request
|
|
|
|
* @param maxFramePayloadLength
|
|
|
|
* Maximum length of a frame's payload
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
* @param handshakeTimeoutMillis
|
|
|
|
* Handshake timeout in mills, when handshake timeout, will trigger user
|
|
|
|
* event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT}
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
|
|
|
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
|
|
|
|
boolean handleCloseFrames, long handshakeTimeoutMillis) {
|
|
|
|
this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength,
|
|
|
|
handleCloseFrames, true, false, handshakeTimeoutMillis);
|
2013-03-20 10:04:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param webSocketURL
|
|
|
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
|
|
|
* sent to this URL.
|
|
|
|
* @param version
|
|
|
|
* Version of web socket specification to use to connect to the server
|
|
|
|
* @param subprotocol
|
|
|
|
* Sub protocol request sent to the server.
|
|
|
|
* @param customHeaders
|
|
|
|
* Map of custom headers to add to the client request
|
|
|
|
* @param maxFramePayloadLength
|
|
|
|
* Maximum length of a frame's payload
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
|
|
|
boolean allowExtensions, HttpHeaders customHeaders,
|
|
|
|
int maxFramePayloadLength) {
|
|
|
|
this(webSocketURL, version, subprotocol,
|
2019-05-22 12:37:28 +02:00
|
|
|
allowExtensions, customHeaders, maxFramePayloadLength, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param webSocketURL
|
|
|
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
|
|
|
* sent to this URL.
|
|
|
|
* @param version
|
|
|
|
* Version of web socket specification to use to connect to the server
|
|
|
|
* @param subprotocol
|
|
|
|
* Sub protocol request sent to the server.
|
|
|
|
* @param customHeaders
|
|
|
|
* Map of custom headers to add to the client request
|
|
|
|
* @param maxFramePayloadLength
|
|
|
|
* Maximum length of a frame's payload
|
|
|
|
* @param handshakeTimeoutMillis
|
|
|
|
* Handshake timeout in mills, when handshake timeout, will trigger user
|
|
|
|
* event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT}
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
|
|
|
boolean allowExtensions, HttpHeaders customHeaders,
|
|
|
|
int maxFramePayloadLength, long handshakeTimeoutMillis) {
|
|
|
|
this(webSocketURL, version, subprotocol,
|
|
|
|
allowExtensions, customHeaders, maxFramePayloadLength, true, handshakeTimeoutMillis);
|
2013-03-20 10:04:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param handshaker
|
|
|
|
* The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection
|
|
|
|
* was established to the remote peer.
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames) {
|
2019-05-22 12:37:28 +02:00
|
|
|
this(handshaker, handleCloseFrames, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param handshaker
|
|
|
|
* The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection
|
|
|
|
* was established to the remote peer.
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
* @param handshakeTimeoutMillis
|
|
|
|
* Handshake timeout in mills, when handshake timeout, will trigger user
|
|
|
|
* event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT}
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames,
|
|
|
|
long handshakeTimeoutMillis) {
|
|
|
|
this(handshaker, handleCloseFrames, true, handshakeTimeoutMillis);
|
2018-05-24 20:27:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param handshaker
|
|
|
|
* The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection
|
|
|
|
* was established to the remote peer.
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
* @param dropPongFrames
|
|
|
|
* {@code true} if pong frames should not be forwarded
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames,
|
|
|
|
boolean dropPongFrames) {
|
2019-05-22 12:37:28 +02:00
|
|
|
this(handshaker, handleCloseFrames, dropPongFrames, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param handshaker
|
|
|
|
* The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection
|
|
|
|
* was established to the remote peer.
|
|
|
|
* @param handleCloseFrames
|
|
|
|
* {@code true} if close frames should not be forwarded and just close the channel
|
|
|
|
* @param dropPongFrames
|
|
|
|
* {@code true} if pong frames should not be forwarded
|
|
|
|
* @param handshakeTimeoutMillis
|
|
|
|
* Handshake timeout in mills, when handshake timeout, will trigger user
|
|
|
|
* event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT}
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames,
|
|
|
|
boolean dropPongFrames, long handshakeTimeoutMillis) {
|
2018-05-24 20:27:29 +02:00
|
|
|
super(dropPongFrames);
|
2013-03-20 10:04:17 +01:00
|
|
|
this.handshaker = handshaker;
|
|
|
|
this.handleCloseFrames = handleCloseFrames;
|
2019-05-22 12:37:28 +02:00
|
|
|
this.handshakeTimeoutMillis = checkPositive(handshakeTimeoutMillis, "handshakeTimeoutMillis");
|
2013-03-20 10:04:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param handshaker
|
|
|
|
* The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection
|
|
|
|
* was established to the remote peer.
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker) {
|
2019-05-22 12:37:28 +02:00
|
|
|
this(handshaker, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base constructor
|
|
|
|
*
|
|
|
|
* @param handshaker
|
|
|
|
* The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection
|
|
|
|
* was established to the remote peer.
|
|
|
|
* @param handshakeTimeoutMillis
|
|
|
|
* Handshake timeout in mills, when handshake timeout, will trigger user
|
|
|
|
* event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT}
|
|
|
|
*/
|
|
|
|
public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, long handshakeTimeoutMillis) {
|
|
|
|
this(handshaker, true, handshakeTimeoutMillis);
|
2013-03-20 10:04:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
Remove MessageList from public API and change ChannelInbound/OutboundHandler accordingly
I must admit MesageList was pain in the ass. Instead of forcing a
handler always loop over the list of messages, this commit splits
messageReceived(ctx, list) into two event handlers:
- messageReceived(ctx, msg)
- mmessageReceivedLast(ctx)
When Netty reads one or more messages, messageReceived(ctx, msg) event
is triggered for each message. Once the current read operation is
finished, messageReceivedLast() is triggered to tell the handler that
the last messageReceived() was the last message in the current batch.
Similarly, for outbound, write(ctx, list) has been split into two:
- write(ctx, msg)
- flush(ctx, promise)
Instead of writing a list of message with a promise, a user is now
supposed to call write(msg) multiple times and then call flush() to
actually flush the buffered messages.
Please note that write() doesn't have a promise with it. You must call
flush() to get notified on completion. (or you can use writeAndFlush())
Other changes:
- Because MessageList is completely hidden, codec framework uses
List<Object> instead of MessageList as an output parameter.
2013-07-08 12:03:40 +02:00
|
|
|
protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List<Object> out) throws Exception {
|
2013-03-20 10:04:17 +01:00
|
|
|
if (handleCloseFrames && frame instanceof CloseWebSocketFrame) {
|
|
|
|
ctx.close();
|
|
|
|
return;
|
|
|
|
}
|
Revamp the core API to reduce memory footprint and consumption
The API changes made so far turned out to increase the memory footprint
and consumption while our intention was actually decreasing them.
Memory consumption issue:
When there are many connections which does not exchange data frequently,
the old Netty 4 API spent a lot more memory than 3 because it always
allocates per-handler buffer for each connection unless otherwise
explicitly stated by a user. In a usual real world load, a client
doesn't always send requests without pausing, so the idea of having a
buffer whose life cycle if bound to the life cycle of a connection
didn't work as expected.
Memory footprint issue:
The old Netty 4 API decreased overall memory footprint by a great deal
in many cases. It was mainly because the old Netty 4 API did not
allocate a new buffer and event object for each read. Instead, it
created a new buffer for each handler in a pipeline. This works pretty
well as long as the number of handlers in a pipeline is only a few.
However, for a highly modular application with many handlers which
handles connections which lasts for relatively short period, it actually
makes the memory footprint issue much worse.
Changes:
All in all, this is about retaining all the good changes we made in 4 so
far such as better thread model and going back to the way how we dealt
with message events in 3.
To fix the memory consumption/footprint issue mentioned above, we made a
hard decision to break the backward compatibility again with the
following changes:
- Remove MessageBuf
- Merge Buf into ByteBuf
- Merge ChannelInboundByte/MessageHandler and ChannelStateHandler into ChannelInboundHandler
- Similar changes were made to the adapter classes
- Merge ChannelOutboundByte/MessageHandler and ChannelOperationHandler into ChannelOutboundHandler
- Similar changes were made to the adapter classes
- Introduce MessageList which is similar to `MessageEvent` in Netty 3
- Replace inboundBufferUpdated(ctx) with messageReceived(ctx, MessageList)
- Replace flush(ctx, promise) with write(ctx, MessageList, promise)
- Remove ByteToByteEncoder/Decoder/Codec
- Replaced by MessageToByteEncoder<ByteBuf>, ByteToMessageDecoder<ByteBuf>, and ByteMessageCodec<ByteBuf>
- Merge EmbeddedByteChannel and EmbeddedMessageChannel into EmbeddedChannel
- Add SimpleChannelInboundHandler which is sometimes more useful than
ChannelInboundHandlerAdapter
- Bring back Channel.isWritable() from Netty 3
- Add ChannelInboundHandler.channelWritabilityChanges() event
- Add RecvByteBufAllocator configuration property
- Similar to ReceiveBufferSizePredictor in Netty 3
- Some existing configuration properties such as
DatagramChannelConfig.receivePacketSize is gone now.
- Remove suspend/resumeIntermediaryDeallocation() in ByteBuf
This change would have been impossible without @normanmaurer's help. He
fixed, ported, and improved many parts of the changes.
2013-05-28 13:40:19 +02:00
|
|
|
super.decode(ctx, frame, out);
|
2013-03-20 10:04:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-04-05 15:46:18 +02:00
|
|
|
public void handlerAdded(ChannelHandlerContext ctx) {
|
2013-03-20 10:04:17 +01:00
|
|
|
ChannelPipeline cp = ctx.pipeline();
|
|
|
|
if (cp.get(WebSocketClientProtocolHandshakeHandler.class) == null) {
|
|
|
|
// Add the WebSocketClientProtocolHandshakeHandler before this one.
|
|
|
|
ctx.pipeline().addBefore(ctx.name(), WebSocketClientProtocolHandshakeHandler.class.getName(),
|
2019-05-22 12:37:28 +02:00
|
|
|
new WebSocketClientProtocolHandshakeHandler(handshaker, handshakeTimeoutMillis));
|
2013-03-20 10:04:17 +01:00
|
|
|
}
|
2015-12-17 14:57:36 +01:00
|
|
|
if (cp.get(Utf8FrameValidator.class) == null) {
|
|
|
|
// Add the UFT8 checking before this one.
|
|
|
|
ctx.pipeline().addBefore(ctx.name(), Utf8FrameValidator.class.getName(),
|
|
|
|
new Utf8FrameValidator());
|
|
|
|
}
|
2013-03-20 10:04:17 +01:00
|
|
|
}
|
|
|
|
}
|