public override void ChannelRead(IChannelHandlerContext ctx, object msg)
        {
            if (msg is IHttpResponse response &&
                WebSocketExtensionUtil.IsWebsocketUpgrade(response.Headers))
            {
                string extensionsHeader = null;
                if (response.Headers.TryGet(HttpHeaderNames.SecWebsocketExtensions, out ICharSequence value))
                {
                    extensionsHeader = value.ToString();
                }

                var pipeline = ctx.Pipeline;
                if (extensionsHeader is object)
                {
                    List <WebSocketExtensionData> extensions =
                        WebSocketExtensionUtil.ExtractExtensions(extensionsHeader);
                    var validExtensions = new List <IWebSocketClientExtension>(extensions.Count);
                    int rsv             = 0;

                    foreach (WebSocketExtensionData extensionData in extensions)
                    {
                        IWebSocketClientExtension validExtension = null;
                        foreach (IWebSocketClientExtensionHandshaker extensionHandshaker in this.extensionHandshakers)
                        {
                            validExtension = extensionHandshaker.HandshakeExtension(extensionData);
                            if (validExtension is object)
                            {
                                break;
                            }
                        }

                        if (validExtension is object && 0u >= (uint)(validExtension.Rsv & rsv))
                        {
                            rsv = rsv | validExtension.Rsv;
                            validExtensions.Add(validExtension);
                        }
                        else
                        {
                            ThrowHelper.ThrowCodecException_InvalidWSExHandshake(extensionsHeader);
                        }
                    }

                    foreach (IWebSocketClientExtension validExtension in validExtensions)
                    {
                        WebSocketExtensionDecoder decoder = validExtension.NewExtensionDecoder();
                        WebSocketExtensionEncoder encoder = validExtension.NewExtensionEncoder();
                        _ = pipeline.AddAfter(ctx.Name, decoder.GetType().Name, decoder);
                        _ = pipeline.AddAfter(ctx.Name, encoder.GetType().Name, encoder);
                    }
                }

                _ = pipeline.Remove(ctx.Name);
            }

            base.ChannelRead(ctx, msg);
        }
        public override Task WriteAsync(IChannelHandlerContext ctx, object msg)
        {
            Action <Task> continuationAction = null;

            if (msg is IHttpResponse response &&
                WebSocketExtensionUtil.IsWebsocketUpgrade(response.Headers) &&
                this.validExtensions != null)
            {
                string headerValue = null;
                if (response.Headers.TryGet(HttpHeaderNames.SecWebsocketExtensions, out ICharSequence value))
                {
                    headerValue = value?.ToString();
                }

                foreach (IWebSocketServerExtension extension in this.validExtensions)
                {
                    WebSocketExtensionData extensionData = extension.NewReponseData();
                    headerValue = WebSocketExtensionUtil.AppendExtension(headerValue,
                                                                         extensionData.Name, extensionData.Parameters);
                }

                continuationAction = promise =>
                {
                    if (promise.Status == TaskStatus.RanToCompletion)
                    {
                        foreach (IWebSocketServerExtension extension in this.validExtensions)
                        {
                            WebSocketExtensionDecoder decoder = extension.NewExtensionDecoder();
                            WebSocketExtensionEncoder encoder = extension.NewExtensionEncoder();
                            ctx.Channel.Pipeline.AddAfter(ctx.Name, decoder.GetType().Name, decoder);
                            ctx.Channel.Pipeline.AddAfter(ctx.Name, encoder.GetType().Name, encoder);
                        }
                    }
                    ctx.Channel.Pipeline.Remove(ctx.Name);
                };

                if (headerValue != null)
                {
                    response.Headers.Set(HttpHeaderNames.SecWebsocketExtensions, headerValue);
                }
            }

            return(continuationAction == null
                ? base.WriteAsync(ctx, msg)
                : base.WriteAsync(ctx, msg)
                   .ContinueWith(continuationAction, TaskContinuationOptions.ExecuteSynchronously));
        }
        private static void SwitchWebSocketExtensionHandler(Task promise, object state)
        {
            var wrapped         = (Tuple <IChannelHandlerContext, List <IWebSocketServerExtension> >)state;
            var ctx             = wrapped.Item1;
            var validExtensions = wrapped.Item2;
            var pipeline        = ctx.Pipeline;

            if (promise.IsSuccess())
            {
                for (int i = 0; i < validExtensions.Count; i++)
                {
                    IWebSocketServerExtension extension = validExtensions[i];
                    WebSocketExtensionDecoder decoder   = extension.NewExtensionDecoder();
                    WebSocketExtensionEncoder encoder   = extension.NewExtensionEncoder();
                    _ = pipeline
                        .AddAfter(ctx.Name, decoder.GetType().Name, decoder)
                        .AddAfter(ctx.Name, encoder.GetType().Name, encoder);
                }
            }
        }