netty5/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolConfig.java
ursa 7ff8cde66f Send close frame on channel close, when this frame was not send manually (#9745)
Motivation:
By default CloseWebSocketFrames are handled automatically.
However I need manually manage their sending both on client- and on server-sides.

Modification:
Send close frame on channel close automatically, when it was not send before explicitly.

Result:
No more messages like "Connection closed by remote peer" for normal close flows.
2019-11-18 20:32:45 +01:00

277 lines
9.6 KiB
Java

/*
* Copyright 2019 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.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent;
import io.netty.util.internal.ObjectUtil;
import static io.netty.util.internal.ObjectUtil.checkPositive;
/**
* WebSocket server configuration.
*/
public final class WebSocketServerProtocolConfig {
static final WebSocketServerProtocolConfig DEFAULT = new WebSocketServerProtocolConfig(
"/", null, false, 10000L, 0, true, WebSocketCloseStatus.NORMAL_CLOSURE, true, WebSocketDecoderConfig.DEFAULT);
private final String websocketPath;
private final String subprotocols;
private final boolean checkStartsWith;
private final long handshakeTimeoutMillis;
private final long forceCloseTimeoutMillis;
private final boolean handleCloseFrames;
private final WebSocketCloseStatus sendCloseFrame;
private final boolean dropPongFrames;
private final WebSocketDecoderConfig decoderConfig;
private WebSocketServerProtocolConfig(
String websocketPath,
String subprotocols,
boolean checkStartsWith,
long handshakeTimeoutMillis,
long forceCloseTimeoutMillis,
boolean handleCloseFrames,
WebSocketCloseStatus sendCloseFrame,
boolean dropPongFrames,
WebSocketDecoderConfig decoderConfig
) {
this.websocketPath = websocketPath;
this.subprotocols = subprotocols;
this.checkStartsWith = checkStartsWith;
this.handshakeTimeoutMillis = checkPositive(handshakeTimeoutMillis, "handshakeTimeoutMillis");
this.forceCloseTimeoutMillis = forceCloseTimeoutMillis;
this.handleCloseFrames = handleCloseFrames;
this.sendCloseFrame = sendCloseFrame;
this.dropPongFrames = dropPongFrames;
this.decoderConfig = decoderConfig == null ? WebSocketDecoderConfig.DEFAULT : decoderConfig;
}
public String websocketPath() {
return websocketPath;
}
public String subprotocols() {
return subprotocols;
}
public boolean checkStartsWith() {
return checkStartsWith;
}
public long handshakeTimeoutMillis() {
return handshakeTimeoutMillis;
}
public long forceCloseTimeoutMillis() {
return forceCloseTimeoutMillis;
}
public boolean handleCloseFrames() {
return handleCloseFrames;
}
public WebSocketCloseStatus sendCloseFrame() {
return sendCloseFrame;
}
public boolean dropPongFrames() {
return dropPongFrames;
}
public WebSocketDecoderConfig decoderConfig() {
return decoderConfig;
}
@Override
public String toString() {
return "WebSocketServerProtocolConfig" +
" {websocketPath=" + websocketPath +
", subprotocols=" + subprotocols +
", checkStartsWith=" + checkStartsWith +
", handshakeTimeoutMillis=" + handshakeTimeoutMillis +
", forceCloseTimeoutMillis=" + forceCloseTimeoutMillis +
", handleCloseFrames=" + handleCloseFrames +
", sendCloseFrame=" + sendCloseFrame +
", dropPongFrames=" + dropPongFrames +
", decoderConfig=" + decoderConfig +
"}";
}
public Builder toBuilder() {
return new Builder(this);
}
public static Builder newBuilder() {
return new Builder(DEFAULT);
}
public static final class Builder {
private String websocketPath;
private String subprotocols;
private boolean checkStartsWith;
private long handshakeTimeoutMillis;
private long forceCloseTimeoutMillis;
private boolean handleCloseFrames;
private WebSocketCloseStatus sendCloseFrame;
private boolean dropPongFrames;
private WebSocketDecoderConfig decoderConfig;
private WebSocketDecoderConfig.Builder decoderConfigBuilder;
private Builder(WebSocketServerProtocolConfig serverConfig) {
ObjectUtil.checkNotNull(serverConfig, "serverConfig");
websocketPath = serverConfig.websocketPath();
subprotocols = serverConfig.subprotocols();
checkStartsWith = serverConfig.checkStartsWith();
handshakeTimeoutMillis = serverConfig.handshakeTimeoutMillis();
forceCloseTimeoutMillis = serverConfig.forceCloseTimeoutMillis();
handleCloseFrames = serverConfig.handleCloseFrames();
sendCloseFrame = serverConfig.sendCloseFrame();
dropPongFrames = serverConfig.dropPongFrames();
decoderConfig = serverConfig.decoderConfig();
}
/**
* URI path component to handle websocket upgrade requests on.
*/
public Builder websocketPath(String websocketPath) {
this.websocketPath = websocketPath;
return this;
}
/**
* CSV of supported protocols
*/
public Builder subprotocols(String subprotocols) {
this.subprotocols = subprotocols;
return this;
}
/**
* {@code true} to handle all requests, where URI path component starts from
* {@link WebSocketServerProtocolConfig#websocketPath()}, {@code false} for exact match (default).
*/
public Builder checkStartsWith(boolean checkStartsWith) {
this.checkStartsWith = checkStartsWith;
return this;
}
/**
* Handshake timeout in mills, when handshake timeout, will trigger user
* event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT}
*/
public Builder handshakeTimeoutMillis(long handshakeTimeoutMillis) {
this.handshakeTimeoutMillis = handshakeTimeoutMillis;
return this;
}
/**
* Close the connection if it was not closed by the client after timeout specified
*/
public Builder forceCloseTimeoutMillis(long forceCloseTimeoutMillis) {
this.forceCloseTimeoutMillis = forceCloseTimeoutMillis;
return this;
}
/**
* {@code true} if close frames should not be forwarded and just close the channel
*/
public Builder handleCloseFrames(boolean handleCloseFrames) {
this.handleCloseFrames = handleCloseFrames;
return this;
}
/**
* Close frame to send, when close frame was not send manually. Or {@code null} to disable proper close.
*/
public Builder sendCloseFrame(WebSocketCloseStatus sendCloseFrame) {
this.sendCloseFrame = sendCloseFrame;
return this;
}
/**
* {@code true} if pong frames should not be forwarded
*/
public Builder dropPongFrames(boolean dropPongFrames) {
this.dropPongFrames = dropPongFrames;
return this;
}
/**
* Frames decoder configuration.
*/
public Builder decoderConfig(WebSocketDecoderConfig decoderConfig) {
this.decoderConfig = decoderConfig == null ? WebSocketDecoderConfig.DEFAULT : decoderConfig;
this.decoderConfigBuilder = null;
return this;
}
private WebSocketDecoderConfig.Builder decoderConfigBuilder() {
if (decoderConfigBuilder == null) {
decoderConfigBuilder = decoderConfig.toBuilder();
}
return decoderConfigBuilder;
}
public Builder maxFramePayloadLength(int maxFramePayloadLength) {
decoderConfigBuilder().maxFramePayloadLength(maxFramePayloadLength);
return this;
}
public Builder expectMaskedFrames(boolean expectMaskedFrames) {
decoderConfigBuilder().expectMaskedFrames(expectMaskedFrames);
return this;
}
public Builder allowMaskMismatch(boolean allowMaskMismatch) {
decoderConfigBuilder().allowMaskMismatch(allowMaskMismatch);
return this;
}
public Builder allowExtensions(boolean allowExtensions) {
decoderConfigBuilder().allowExtensions(allowExtensions);
return this;
}
public Builder closeOnProtocolViolation(boolean closeOnProtocolViolation) {
decoderConfigBuilder().closeOnProtocolViolation(closeOnProtocolViolation);
return this;
}
public Builder withUTF8Validator(boolean withUTF8Validator) {
decoderConfigBuilder().withUTF8Validator(withUTF8Validator);
return this;
}
/**
* Build unmodifiable server protocol configuration.
*/
public WebSocketServerProtocolConfig build() {
return new WebSocketServerProtocolConfig(
websocketPath,
subprotocols,
checkStartsWith,
handshakeTimeoutMillis,
forceCloseTimeoutMillis,
handleCloseFrames,
sendCloseFrame,
dropPongFrames,
decoderConfigBuilder == null ? decoderConfig : decoderConfigBuilder.build()
);
}
}
}