Minor performance improvement in websocket upgrade (#10710)
Motivation: I noticed WebSocketServerExtensionHandler taking up a non-trivial amount of CPU time for a non-websocket based menchmark. This attempts to speed it up. Modifications: - It is faster to check for a 101 response than to look at headers, so an initial response code check is done - Move all the actual upgrade code into its own method to increase chance of this method being inlined - Add an extra contains() check for the upgrade header, to avoid allocating an iterator if there is no upgrade header Result: A small but noticable performance increase. Signed-off-by: Stuart Douglas <stuart.w.douglas@gmail.com>
This commit is contained in:
parent
c061bd1798
commit
7d971a78a0
@ -37,7 +37,10 @@ public final class WebSocketExtensionUtil {
|
|||||||
private static final Pattern PARAMETER = Pattern.compile("^([^=]+)(=[\\\"]?([^\\\"]+)[\\\"]?)?$");
|
private static final Pattern PARAMETER = Pattern.compile("^([^=]+)(=[\\\"]?([^\\\"]+)[\\\"]?)?$");
|
||||||
|
|
||||||
static boolean isWebsocketUpgrade(HttpHeaders headers) {
|
static boolean isWebsocketUpgrade(HttpHeaders headers) {
|
||||||
return headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true) &&
|
//this contains check does not allocate an iterator, and most requests are not upgrades
|
||||||
|
//so we do the contains check first before checking for specific values
|
||||||
|
return headers.contains(HttpHeaderNames.UPGRADE) &&
|
||||||
|
headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true) &&
|
||||||
headers.contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true);
|
headers.contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import io.netty.handler.codec.http.HttpHeaderNames;
|
|||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -104,13 +105,23 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
if (msg instanceof HttpResponse) {
|
if (msg instanceof HttpResponse) {
|
||||||
HttpHeaders headers = ((HttpResponse) msg).headers();
|
HttpResponse httpResponse = (HttpResponse) msg;
|
||||||
|
//checking the status is faster than looking at headers
|
||||||
|
//so we do this first
|
||||||
|
if (HttpResponseStatus.SWITCHING_PROTOCOLS.equals(httpResponse.status())) {
|
||||||
|
handlePotentialUpgrade(ctx, promise, httpResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.write(msg, promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePotentialUpgrade(final ChannelHandlerContext ctx,
|
||||||
|
ChannelPromise promise, HttpResponse httpResponse) {
|
||||||
|
HttpHeaders headers = httpResponse.headers();
|
||||||
if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
|
if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
|
||||||
|
|
||||||
if (validExtensions != null) {
|
if (validExtensions != null) {
|
||||||
String headerValue = headers.getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
|
String headerValue = headers.getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
|
||||||
|
|
||||||
for (WebSocketServerExtension extension : validExtensions) {
|
for (WebSocketServerExtension extension : validExtensions) {
|
||||||
WebSocketExtensionData extensionData = extension.newResponseData();
|
WebSocketExtensionData extensionData = extension.newResponseData();
|
||||||
headerValue = WebSocketExtensionUtil.appendExtension(headerValue,
|
headerValue = WebSocketExtensionUtil.appendExtension(headerValue,
|
||||||
@ -122,9 +133,10 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
for (WebSocketServerExtension extension : validExtensions) {
|
for (WebSocketServerExtension extension : validExtensions) {
|
||||||
WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
|
WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
|
||||||
WebSocketExtensionEncoder encoder = extension.newExtensionEncoder();
|
WebSocketExtensionEncoder encoder = extension.newExtensionEncoder();
|
||||||
|
String name = ctx.name();
|
||||||
ctx.pipeline()
|
ctx.pipeline()
|
||||||
.addAfter(ctx.name(), decoder.getClass().getName(), decoder)
|
.addAfter(name, decoder.getClass().getName(), decoder)
|
||||||
.addAfter(ctx.name(), encoder.getClass().getName(), encoder);
|
.addAfter(name, encoder.getClass().getName(), encoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -133,7 +145,6 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, headerValue);
|
headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, headerValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.addListener((ChannelFutureListener) future -> {
|
promise.addListener((ChannelFutureListener) future -> {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ctx.pipeline().remove(WebSocketServerExtensionHandler.this);
|
ctx.pipeline().remove(WebSocketServerExtensionHandler.this);
|
||||||
@ -141,7 +152,4 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.write(msg, promise);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user