/*
 * 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:
 *
 *   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.extensions;

import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

This handler negotiates and initializes the WebSocket Extensions. This implementation negotiates the extension with the server in a defined order, ensures that the successfully negotiated extensions are consistent between them, and initializes the channel pipeline with the extension decoder and encoder. Find a basic implementation for compression extensions at io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler.
/** * This handler negotiates and initializes the WebSocket Extensions. * * This implementation negotiates the extension with the server in a defined order, * ensures that the successfully negotiated extensions are consistent between them, * and initializes the channel pipeline with the extension decoder and encoder. * * Find a basic implementation for compression extensions at * <tt>io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler</tt>. */
public class WebSocketClientExtensionHandler extends ChannelDuplexHandler { private final List<WebSocketClientExtensionHandshaker> extensionHandshakers;
Constructor
Params:
  • extensionHandshakers – The extension handshaker in priority order. A handshaker could be repeated many times with fallback configuration.
/** * Constructor * * @param extensionHandshakers * The extension handshaker in priority order. A handshaker could be repeated many times * with fallback configuration. */
public WebSocketClientExtensionHandler(WebSocketClientExtensionHandshaker... extensionHandshakers) { if (extensionHandshakers == null) { throw new NullPointerException("extensionHandshakers"); } if (extensionHandshakers.length == 0) { throw new IllegalArgumentException("extensionHandshakers must contains at least one handshaker"); } this.extensionHandshakers = Arrays.asList(extensionHandshakers); } @Override public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) { HttpRequest request = (HttpRequest) msg; String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); for (WebSocketClientExtensionHandshaker extensionHandshaker : extensionHandshakers) { WebSocketExtensionData extensionData = extensionHandshaker.newRequestData(); headerValue = WebSocketExtensionUtil.appendExtension(headerValue, extensionData.name(), extensionData.parameters()); } request.headers().set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, headerValue); } super.write(ctx, msg, promise); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof HttpResponse) { HttpResponse response = (HttpResponse) msg; if (WebSocketExtensionUtil.isWebsocketUpgrade(response.headers())) { String extensionsHeader = response.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); if (extensionsHeader != null) { List<WebSocketExtensionData> extensions = WebSocketExtensionUtil.extractExtensions(extensionsHeader); List<WebSocketClientExtension> validExtensions = new ArrayList<WebSocketClientExtension>(extensions.size()); int rsv = 0; for (WebSocketExtensionData extensionData : extensions) { Iterator<WebSocketClientExtensionHandshaker> extensionHandshakersIterator = extensionHandshakers.iterator(); WebSocketClientExtension validExtension = null; while (validExtension == null && extensionHandshakersIterator.hasNext()) { WebSocketClientExtensionHandshaker extensionHandshaker = extensionHandshakersIterator.next(); validExtension = extensionHandshaker.handshakeExtension(extensionData); } if (validExtension != null && ((validExtension.rsv() & rsv) == 0)) { rsv = rsv | validExtension.rsv(); validExtensions.add(validExtension); } else { throw new CodecException( "invalid WebSocket Extension handshake for \"" + extensionsHeader + '"'); } } for (WebSocketClientExtension validExtension : validExtensions) { WebSocketExtensionDecoder decoder = validExtension.newExtensionDecoder(); WebSocketExtensionEncoder encoder = validExtension.newExtensionEncoder(); ctx.pipeline().addAfter(ctx.name(), decoder.getClass().getName(), decoder); ctx.pipeline().addAfter(ctx.name(), encoder.getClass().getName(), encoder); } } ctx.pipeline().remove(ctx.name()); } } super.channelRead(ctx, msg); } }