io.netty.handler.codec.http.HttpServerUpgradeHandler Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* Copyright 2014 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:
*
* https://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;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static io.netty.util.AsciiString.containsAllContentEqualsIgnoreCase;
import static io.netty.util.AsciiString.containsContentEqualsIgnoreCase;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.StringUtil.COMMA;
/**
* A server-side handler that receives HTTP requests and optionally performs a protocol switch if
* the requested protocol is supported. Once an upgrade is performed, this handler removes itself
* from the pipeline.
*/
public class HttpServerUpgradeHandler extends HttpObjectAggregator {
/**
* The source codec that is used in the pipeline initially.
*/
public interface SourceCodec {
/**
* Removes this codec (i.e. all associated handlers) from the pipeline.
*/
void upgradeFrom(ChannelHandlerContext ctx);
}
/**
* A codec that the source can be upgraded to.
*/
public interface UpgradeCodec {
/**
* Gets all protocol-specific headers required by this protocol for a successful upgrade.
* Any supplied header will be required to appear in the {@link HttpHeaderNames#CONNECTION} header as well.
*/
Collection requiredUpgradeHeaders();
/**
* Prepares the {@code upgradeHeaders} for a protocol update based upon the contents of {@code upgradeRequest}.
* This method returns a boolean value to proceed or abort the upgrade in progress. If {@code false} is
* returned, the upgrade is aborted and the {@code upgradeRequest} will be passed through the inbound pipeline
* as if no upgrade was performed. If {@code true} is returned, the upgrade will proceed to the next
* step which invokes {@link #upgradeTo}. When returning {@code true}, you can add headers to
* the {@code upgradeHeaders} so that they are added to the 101 Switching protocols response.
*/
boolean prepareUpgradeResponse(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest,
HttpHeaders upgradeHeaders);
/**
* Performs an HTTP protocol upgrade from the source codec. This method is responsible for
* adding all handlers required for the new protocol.
*
* @param ctx the context for the current handler.
* @param upgradeRequest the request that triggered the upgrade to this protocol.
*/
void upgradeTo(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest);
}
/**
* Creates a new {@link UpgradeCodec} for the requested protocol name.
*/
public interface UpgradeCodecFactory {
/**
* Invoked by {@link HttpServerUpgradeHandler} for all the requested protocol names in the order of
* the client preference. The first non-{@code null} {@link UpgradeCodec} returned by this method
* will be selected.
*
* @return a new {@link UpgradeCodec}, or {@code null} if the specified protocol name is not supported
*/
UpgradeCodec newUpgradeCodec(CharSequence protocol);
}
/**
* User event that is fired to notify about the completion of an HTTP upgrade
* to another protocol. Contains the original upgrade request so that the response
* (if required) can be sent using the new protocol.
*/
public static final class UpgradeEvent implements ReferenceCounted {
private final CharSequence protocol;
private final FullHttpRequest upgradeRequest;
UpgradeEvent(CharSequence protocol, FullHttpRequest upgradeRequest) {
this.protocol = protocol;
this.upgradeRequest = upgradeRequest;
}
/**
* The protocol that the channel has been upgraded to.
*/
public CharSequence protocol() {
return protocol;
}
/**
* Gets the request that triggered the protocol upgrade.
*/
public FullHttpRequest upgradeRequest() {
return upgradeRequest;
}
@Override
public int refCnt() {
return upgradeRequest.refCnt();
}
@Override
public UpgradeEvent retain() {
upgradeRequest.retain();
return this;
}
@Override
public UpgradeEvent retain(int increment) {
upgradeRequest.retain(increment);
return this;
}
@Override
public UpgradeEvent touch() {
upgradeRequest.touch();
return this;
}
@Override
public UpgradeEvent touch(Object hint) {
upgradeRequest.touch(hint);
return this;
}
@Override
public boolean release() {
return upgradeRequest.release();
}
@Override
public boolean release(int decrement) {
return upgradeRequest.release(decrement);
}
@Override
public String toString() {
return "UpgradeEvent [protocol=" + protocol + ", upgradeRequest=" + upgradeRequest + ']';
}
}
private final SourceCodec sourceCodec;
private final UpgradeCodecFactory upgradeCodecFactory;
private final HttpHeadersFactory headersFactory;
private final HttpHeadersFactory trailersFactory;
private boolean handlingUpgrade;
/**
* Constructs the upgrader with the supported codecs.
*
* The handler instantiated by this constructor will reject an upgrade request with non-empty content.
* It should not be a concern because an upgrade request is most likely a GET request.
* If you have a client that sends a non-GET upgrade request, please consider using
* {@link #HttpServerUpgradeHandler(SourceCodec, UpgradeCodecFactory, int)} to specify the maximum
* length of the content of an upgrade request.
*
*
* @param sourceCodec the codec that is being used initially
* @param upgradeCodecFactory the factory that creates a new upgrade codec
* for one of the requested upgrade protocols
*/
public HttpServerUpgradeHandler(SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory) {
this(sourceCodec, upgradeCodecFactory, 0,
DefaultHttpHeadersFactory.headersFactory(), DefaultHttpHeadersFactory.trailersFactory());
}
/**
* Constructs the upgrader with the supported codecs.
*
* @param sourceCodec the codec that is being used initially
* @param upgradeCodecFactory the factory that creates a new upgrade codec
* for one of the requested upgrade protocols
* @param maxContentLength the maximum length of the content of an upgrade request
*/
public HttpServerUpgradeHandler(
SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory, int maxContentLength) {
this(sourceCodec, upgradeCodecFactory, maxContentLength,
DefaultHttpHeadersFactory.headersFactory(), DefaultHttpHeadersFactory.trailersFactory());
}
/**
* Constructs the upgrader with the supported codecs.
*
* @param sourceCodec the codec that is being used initially
* @param upgradeCodecFactory the factory that creates a new upgrade codec
* for one of the requested upgrade protocols
* @param maxContentLength the maximum length of the content of an upgrade request
* @param validateHeaders validate the header names and values of the upgrade response.
*/
public HttpServerUpgradeHandler(SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory,
int maxContentLength, boolean validateHeaders) {
this(sourceCodec, upgradeCodecFactory, maxContentLength,
DefaultHttpHeadersFactory.headersFactory().withValidation(validateHeaders),
DefaultHttpHeadersFactory.trailersFactory().withValidation(validateHeaders));
}
/**
* Constructs the upgrader with the supported codecs.
*
* @param sourceCodec the codec that is being used initially
* @param upgradeCodecFactory the factory that creates a new upgrade codec
* for one of the requested upgrade protocols
* @param maxContentLength the maximum length of the content of an upgrade request
* @param headersFactory The {@link HttpHeadersFactory} to use for headers.
* The recommended default factory is {@link DefaultHttpHeadersFactory#headersFactory()}.
* @param trailersFactory The {@link HttpHeadersFactory} to use for trailers.
* The recommended default factory is {@link DefaultHttpHeadersFactory#trailersFactory()}.
*/
public HttpServerUpgradeHandler(
SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory, int maxContentLength,
HttpHeadersFactory headersFactory, HttpHeadersFactory trailersFactory) {
super(maxContentLength);
this.sourceCodec = checkNotNull(sourceCodec, "sourceCodec");
this.upgradeCodecFactory = checkNotNull(upgradeCodecFactory, "upgradeCodecFactory");
this.headersFactory = checkNotNull(headersFactory, "headersFactory");
this.trailersFactory = checkNotNull(trailersFactory, "trailersFactory");
}
@Override
protected void decode(ChannelHandlerContext ctx, HttpObject msg, List