103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.websocket;
2003928aee4356845252ac6b662d5c72c29903813eJake Slack
2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.EOFException;
2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.net.ProtocolException;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.nio.channels.SelectionKey;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.nio.channels.SocketChannel;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.List;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Map;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Queue;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Random;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.ConcurrentLinkedQueue;
3103928aee4356845252ac6b662d5c72c29903813eJake Slack
3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.net.ssl.SSLEngine;
3303928aee4356845252ac6b662d5c72c29903813eJake Slack
3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.http.HttpFields;
3503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.http.HttpParser;
3603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.AbstractConnection;
3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.AsyncEndPoint;
3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.Buffer;
3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.Buffers;
4003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.ByteArrayBuffer;
4103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.ConnectedEndPoint;
4203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.Connection;
4303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.EndPoint;
4403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.SimpleBuffers;
4503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.nio.AsyncConnection;
4603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.nio.SelectChannelEndPoint;
4703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.nio.SelectorManager;
4803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.nio.SslConnection;
4903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.B64Code;
5003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.QuotedStringTokenizer;
5103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.component.AggregateLifeCycle;
5203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
5303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.ssl.SslContextFactory;
5403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.thread.QueuedThreadPool;
5503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.thread.ThreadPool;
5603928aee4356845252ac6b662d5c72c29903813eJake Slack
5703928aee4356845252ac6b662d5c72c29903813eJake Slack/* ------------------------------------------------------------ */
5803928aee4356845252ac6b662d5c72c29903813eJake Slack/**
5903928aee4356845252ac6b662d5c72c29903813eJake Slack * <p>WebSocketClientFactory contains the common components needed by multiple {@link WebSocketClient} instances
6003928aee4356845252ac6b662d5c72c29903813eJake Slack * (for example, a {@link ThreadPool}, a {@link SelectorManager NIO selector}, etc).</p>
6103928aee4356845252ac6b662d5c72c29903813eJake Slack * <p>WebSocketClients with different configurations should share the same factory to avoid to waste resources.</p>
6203928aee4356845252ac6b662d5c72c29903813eJake Slack * <p>If a ThreadPool or MaskGen is passed in the constructor, then it is not added with {@link AggregateLifeCycle#addBean(Object)},
6303928aee4356845252ac6b662d5c72c29903813eJake Slack * so it's lifecycle must be controlled externally.
6403928aee4356845252ac6b662d5c72c29903813eJake Slack *
6503928aee4356845252ac6b662d5c72c29903813eJake Slack * @see WebSocketClient
6603928aee4356845252ac6b662d5c72c29903813eJake Slack */
6703928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class WebSocketClientFactory extends AggregateLifeCycle
6803928aee4356845252ac6b662d5c72c29903813eJake Slack{
6903928aee4356845252ac6b662d5c72c29903813eJake Slack    private final static Logger __log = org.eclipse.jetty.util.log.Log.getLogger(WebSocketClientFactory.class.getName());
7003928aee4356845252ac6b662d5c72c29903813eJake Slack    private final static ByteArrayBuffer __ACCEPT = new ByteArrayBuffer.CaseInsensitive("Sec-WebSocket-Accept");
7103928aee4356845252ac6b662d5c72c29903813eJake Slack    private final Queue<WebSocketConnection> connections = new ConcurrentLinkedQueue<WebSocketConnection>();
7203928aee4356845252ac6b662d5c72c29903813eJake Slack    private final SslContextFactory _sslContextFactory = new SslContextFactory();
7303928aee4356845252ac6b662d5c72c29903813eJake Slack    private final ThreadPool _threadPool;
7403928aee4356845252ac6b662d5c72c29903813eJake Slack    private final WebSocketClientSelector _selector;
7503928aee4356845252ac6b662d5c72c29903813eJake Slack    private MaskGen _maskGen;
7603928aee4356845252ac6b662d5c72c29903813eJake Slack    private WebSocketBuffers _buffers;
7703928aee4356845252ac6b662d5c72c29903813eJake Slack
7803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
7903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
8003928aee4356845252ac6b662d5c72c29903813eJake Slack     * <p>Creates a WebSocketClientFactory with the default configuration.</p>
8103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
8203928aee4356845252ac6b662d5c72c29903813eJake Slack    public WebSocketClientFactory()
8303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
8403928aee4356845252ac6b662d5c72c29903813eJake Slack        this(null);
8503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
8603928aee4356845252ac6b662d5c72c29903813eJake Slack
8703928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
8803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
8903928aee4356845252ac6b662d5c72c29903813eJake Slack     * <p>Creates a WebSocketClientFactory with the given ThreadPool and the default configuration.</p>
9003928aee4356845252ac6b662d5c72c29903813eJake Slack     *
9103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param threadPool the ThreadPool instance to use
9203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
9303928aee4356845252ac6b662d5c72c29903813eJake Slack    public WebSocketClientFactory(ThreadPool threadPool)
9403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
9503928aee4356845252ac6b662d5c72c29903813eJake Slack        this(threadPool, new RandomMaskGen());
9603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
9703928aee4356845252ac6b662d5c72c29903813eJake Slack
9803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
9903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
10003928aee4356845252ac6b662d5c72c29903813eJake Slack     * <p>Creates a WebSocketClientFactory with the given ThreadPool and the given MaskGen.</p>
10103928aee4356845252ac6b662d5c72c29903813eJake Slack     *
10203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param threadPool the ThreadPool instance to use
10303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param maskGen    the MaskGen instance to use
10403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
10503928aee4356845252ac6b662d5c72c29903813eJake Slack    public WebSocketClientFactory(ThreadPool threadPool, MaskGen maskGen)
10603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
10703928aee4356845252ac6b662d5c72c29903813eJake Slack        this(threadPool, maskGen, 8192);
10803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
10903928aee4356845252ac6b662d5c72c29903813eJake Slack
11003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
11103928aee4356845252ac6b662d5c72c29903813eJake Slack
11203928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
11303928aee4356845252ac6b662d5c72c29903813eJake Slack     * <p>Creates a WebSocketClientFactory with the specified configuration.</p>
11403928aee4356845252ac6b662d5c72c29903813eJake Slack     *
11503928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param threadPool the ThreadPool instance to use
11603928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param maskGen    the mask generator to use
11703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param bufferSize the read buffer size
11803928aee4356845252ac6b662d5c72c29903813eJake Slack     */
11903928aee4356845252ac6b662d5c72c29903813eJake Slack    public WebSocketClientFactory(ThreadPool threadPool, MaskGen maskGen, int bufferSize)
12003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
12103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (threadPool == null)
12203928aee4356845252ac6b662d5c72c29903813eJake Slack            threadPool = new QueuedThreadPool();
12303928aee4356845252ac6b662d5c72c29903813eJake Slack        _threadPool = threadPool;
12403928aee4356845252ac6b662d5c72c29903813eJake Slack        addBean(_threadPool);
12503928aee4356845252ac6b662d5c72c29903813eJake Slack
12603928aee4356845252ac6b662d5c72c29903813eJake Slack        _buffers = new WebSocketBuffers(bufferSize);
12703928aee4356845252ac6b662d5c72c29903813eJake Slack        addBean(_buffers);
12803928aee4356845252ac6b662d5c72c29903813eJake Slack
12903928aee4356845252ac6b662d5c72c29903813eJake Slack        _maskGen = maskGen;
13003928aee4356845252ac6b662d5c72c29903813eJake Slack        addBean(_maskGen);
13103928aee4356845252ac6b662d5c72c29903813eJake Slack
13203928aee4356845252ac6b662d5c72c29903813eJake Slack        _selector = new WebSocketClientSelector();
13303928aee4356845252ac6b662d5c72c29903813eJake Slack        addBean(_selector);
13403928aee4356845252ac6b662d5c72c29903813eJake Slack
13503928aee4356845252ac6b662d5c72c29903813eJake Slack        addBean(_sslContextFactory);
13603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
13703928aee4356845252ac6b662d5c72c29903813eJake Slack
13803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
13903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
14003928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the SslContextFactory used to configure SSL parameters
14103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
14203928aee4356845252ac6b662d5c72c29903813eJake Slack    public SslContextFactory getSslContextFactory()
14303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
14403928aee4356845252ac6b662d5c72c29903813eJake Slack        return _sslContextFactory;
14503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
14603928aee4356845252ac6b662d5c72c29903813eJake Slack
14703928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
14803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
14903928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get the selectorManager. Used to configure the manager.
15003928aee4356845252ac6b662d5c72c29903813eJake Slack     *
15103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return The {@link SelectorManager} instance.
15203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
15303928aee4356845252ac6b662d5c72c29903813eJake Slack    public SelectorManager getSelectorManager()
15403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
15503928aee4356845252ac6b662d5c72c29903813eJake Slack        return _selector;
15603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
15703928aee4356845252ac6b662d5c72c29903813eJake Slack
15803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
15903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
16003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get the ThreadPool.
16103928aee4356845252ac6b662d5c72c29903813eJake Slack     * Used to set/query the thread pool configuration.
16203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
16303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return The {@link ThreadPool}
16403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
16503928aee4356845252ac6b662d5c72c29903813eJake Slack    public ThreadPool getThreadPool()
16603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
16703928aee4356845252ac6b662d5c72c29903813eJake Slack        return _threadPool;
16803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
16903928aee4356845252ac6b662d5c72c29903813eJake Slack
17003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
17103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
17203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the shared mask generator, or null if no shared mask generator is used
17303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see WebSocketClient#getMaskGen()
17403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
17503928aee4356845252ac6b662d5c72c29903813eJake Slack    public MaskGen getMaskGen()
17603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
17703928aee4356845252ac6b662d5c72c29903813eJake Slack        return _maskGen;
17803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
17903928aee4356845252ac6b662d5c72c29903813eJake Slack
18003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
18103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
18203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param maskGen the shared mask generator, or null if no shared mask generator is used
18303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see WebSocketClient#setMaskGen(MaskGen)
18403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
18503928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setMaskGen(MaskGen maskGen)
18603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
18703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (isRunning())
18803928aee4356845252ac6b662d5c72c29903813eJake Slack            throw new IllegalStateException(getState());
18903928aee4356845252ac6b662d5c72c29903813eJake Slack        removeBean(_maskGen);
19003928aee4356845252ac6b662d5c72c29903813eJake Slack        _maskGen = maskGen;
19103928aee4356845252ac6b662d5c72c29903813eJake Slack        addBean(maskGen);
19203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
19303928aee4356845252ac6b662d5c72c29903813eJake Slack
19403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
19503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
19603928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param bufferSize the read buffer size
19703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see #getBufferSize()
19803928aee4356845252ac6b662d5c72c29903813eJake Slack     */
19903928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setBufferSize(int bufferSize)
20003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
20103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (isRunning())
20203928aee4356845252ac6b662d5c72c29903813eJake Slack            throw new IllegalStateException(getState());
20303928aee4356845252ac6b662d5c72c29903813eJake Slack        removeBean(_buffers);
20403928aee4356845252ac6b662d5c72c29903813eJake Slack        _buffers = new WebSocketBuffers(bufferSize);
20503928aee4356845252ac6b662d5c72c29903813eJake Slack        addBean(_buffers);
20603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
20703928aee4356845252ac6b662d5c72c29903813eJake Slack
20803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
20903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
21003928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the read buffer size
21103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
21203928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getBufferSize()
21303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
21403928aee4356845252ac6b662d5c72c29903813eJake Slack        return _buffers.getBufferSize();
21503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
21603928aee4356845252ac6b662d5c72c29903813eJake Slack
21703928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
21803928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void doStop() throws Exception
21903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
22003928aee4356845252ac6b662d5c72c29903813eJake Slack        closeConnections();
22103928aee4356845252ac6b662d5c72c29903813eJake Slack        super.doStop();
22203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
22303928aee4356845252ac6b662d5c72c29903813eJake Slack
22403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
22503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
22603928aee4356845252ac6b662d5c72c29903813eJake Slack     * <p>Creates and returns a new instance of a {@link WebSocketClient}, configured with this
22703928aee4356845252ac6b662d5c72c29903813eJake Slack     * WebSocketClientFactory instance.</p>
22803928aee4356845252ac6b662d5c72c29903813eJake Slack     *
22903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return a new {@link WebSocketClient} instance
23003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
23103928aee4356845252ac6b662d5c72c29903813eJake Slack    public WebSocketClient newWebSocketClient()
23203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
23303928aee4356845252ac6b662d5c72c29903813eJake Slack        return new WebSocketClient(this);
23403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
23503928aee4356845252ac6b662d5c72c29903813eJake Slack
23603928aee4356845252ac6b662d5c72c29903813eJake Slack    protected SSLEngine newSslEngine(SocketChannel channel) throws IOException
23703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
23803928aee4356845252ac6b662d5c72c29903813eJake Slack        SSLEngine sslEngine;
23903928aee4356845252ac6b662d5c72c29903813eJake Slack        if (channel != null)
24003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
24103928aee4356845252ac6b662d5c72c29903813eJake Slack            String peerHost = channel.socket().getInetAddress().getHostAddress();
24203928aee4356845252ac6b662d5c72c29903813eJake Slack            int peerPort = channel.socket().getPort();
24303928aee4356845252ac6b662d5c72c29903813eJake Slack            sslEngine = _sslContextFactory.newSslEngine(peerHost, peerPort);
24403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
24503928aee4356845252ac6b662d5c72c29903813eJake Slack        else
24603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
24703928aee4356845252ac6b662d5c72c29903813eJake Slack            sslEngine = _sslContextFactory.newSslEngine();
24803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
24903928aee4356845252ac6b662d5c72c29903813eJake Slack        sslEngine.setUseClientMode(true);
25003928aee4356845252ac6b662d5c72c29903813eJake Slack        sslEngine.beginHandshake();
25103928aee4356845252ac6b662d5c72c29903813eJake Slack
25203928aee4356845252ac6b662d5c72c29903813eJake Slack        return sslEngine;
25303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
25403928aee4356845252ac6b662d5c72c29903813eJake Slack
25503928aee4356845252ac6b662d5c72c29903813eJake Slack    protected boolean addConnection(WebSocketConnection connection)
25603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
25703928aee4356845252ac6b662d5c72c29903813eJake Slack        return isRunning() && connections.add(connection);
25803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
25903928aee4356845252ac6b662d5c72c29903813eJake Slack
26003928aee4356845252ac6b662d5c72c29903813eJake Slack    protected boolean removeConnection(WebSocketConnection connection)
26103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
26203928aee4356845252ac6b662d5c72c29903813eJake Slack        return connections.remove(connection);
26303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
26403928aee4356845252ac6b662d5c72c29903813eJake Slack
26503928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void closeConnections()
26603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
26703928aee4356845252ac6b662d5c72c29903813eJake Slack        for (WebSocketConnection connection : connections)
26803928aee4356845252ac6b662d5c72c29903813eJake Slack            connection.shutdown();
26903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
27003928aee4356845252ac6b662d5c72c29903813eJake Slack
27103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
27203928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
27303928aee4356845252ac6b662d5c72c29903813eJake Slack     * WebSocket Client Selector Manager
27403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
27503928aee4356845252ac6b662d5c72c29903813eJake Slack    class WebSocketClientSelector extends SelectorManager
27603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
27703928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
27803928aee4356845252ac6b662d5c72c29903813eJake Slack        public boolean dispatch(Runnable task)
27903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
28003928aee4356845252ac6b662d5c72c29903813eJake Slack            return _threadPool.dispatch(task);
28103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
28203928aee4356845252ac6b662d5c72c29903813eJake Slack
28303928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
28403928aee4356845252ac6b662d5c72c29903813eJake Slack        protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, final SelectionKey key) throws IOException
28503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
28603928aee4356845252ac6b662d5c72c29903813eJake Slack            WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture)key.attachment();
28703928aee4356845252ac6b662d5c72c29903813eJake Slack            int maxIdleTime = holder.getMaxIdleTime();
28803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (maxIdleTime < 0)
28903928aee4356845252ac6b662d5c72c29903813eJake Slack                maxIdleTime = (int)getMaxIdleTime();
29003928aee4356845252ac6b662d5c72c29903813eJake Slack            SelectChannelEndPoint result = new SelectChannelEndPoint(channel, selectSet, key, maxIdleTime);
29103928aee4356845252ac6b662d5c72c29903813eJake Slack            AsyncEndPoint endPoint = result;
29203928aee4356845252ac6b662d5c72c29903813eJake Slack
29303928aee4356845252ac6b662d5c72c29903813eJake Slack            // Detect if it is SSL, and wrap the connection if so
29403928aee4356845252ac6b662d5c72c29903813eJake Slack            if ("wss".equals(holder.getURI().getScheme()))
29503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
29603928aee4356845252ac6b662d5c72c29903813eJake Slack                SSLEngine sslEngine = newSslEngine(channel);
29703928aee4356845252ac6b662d5c72c29903813eJake Slack                SslConnection sslConnection = new SslConnection(sslEngine, endPoint);
29803928aee4356845252ac6b662d5c72c29903813eJake Slack                endPoint.setConnection(sslConnection);
29903928aee4356845252ac6b662d5c72c29903813eJake Slack                endPoint = sslConnection.getSslEndPoint();
30003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
30103928aee4356845252ac6b662d5c72c29903813eJake Slack
30203928aee4356845252ac6b662d5c72c29903813eJake Slack            AsyncConnection connection = selectSet.getManager().newConnection(channel, endPoint, holder);
30303928aee4356845252ac6b662d5c72c29903813eJake Slack            endPoint.setConnection(connection);
30403928aee4356845252ac6b662d5c72c29903813eJake Slack
30503928aee4356845252ac6b662d5c72c29903813eJake Slack            return result;
30603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
30703928aee4356845252ac6b662d5c72c29903813eJake Slack
30803928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
30903928aee4356845252ac6b662d5c72c29903813eJake Slack        public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment)
31003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
31103928aee4356845252ac6b662d5c72c29903813eJake Slack            WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture)attachment;
31203928aee4356845252ac6b662d5c72c29903813eJake Slack            return new HandshakeConnection(endpoint, holder);
31303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
31403928aee4356845252ac6b662d5c72c29903813eJake Slack
31503928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
31603928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void endPointOpened(SelectChannelEndPoint endpoint)
31703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
31803928aee4356845252ac6b662d5c72c29903813eJake Slack            // TODO expose on outer class ??
31903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
32003928aee4356845252ac6b662d5c72c29903813eJake Slack
32103928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
32203928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
32303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
32403928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug("upgrade {} -> {}", oldConnection, endpoint.getConnection());
32503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
32603928aee4356845252ac6b662d5c72c29903813eJake Slack
32703928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
32803928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void endPointClosed(SelectChannelEndPoint endpoint)
32903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
33003928aee4356845252ac6b662d5c72c29903813eJake Slack            endpoint.getConnection().onClose();
33103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
33203928aee4356845252ac6b662d5c72c29903813eJake Slack
33303928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
33403928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
33503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
33603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (!(attachment instanceof WebSocketClient.WebSocketFuture))
33703928aee4356845252ac6b662d5c72c29903813eJake Slack                super.connectionFailed(channel, ex, attachment);
33803928aee4356845252ac6b662d5c72c29903813eJake Slack            else
33903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
34003928aee4356845252ac6b662d5c72c29903813eJake Slack                __log.debug(ex);
34103928aee4356845252ac6b662d5c72c29903813eJake Slack                WebSocketClient.WebSocketFuture future = (WebSocketClient.WebSocketFuture)attachment;
34203928aee4356845252ac6b662d5c72c29903813eJake Slack
34303928aee4356845252ac6b662d5c72c29903813eJake Slack                future.handshakeFailed(ex);
34403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
34503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
34603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
34703928aee4356845252ac6b662d5c72c29903813eJake Slack
34803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
34903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
35003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Handshake Connection.
35103928aee4356845252ac6b662d5c72c29903813eJake Slack     * Handles the connection until the handshake succeeds or fails.
35203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
35303928aee4356845252ac6b662d5c72c29903813eJake Slack    class HandshakeConnection extends AbstractConnection implements AsyncConnection
35403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
35503928aee4356845252ac6b662d5c72c29903813eJake Slack        private final AsyncEndPoint _endp;
35603928aee4356845252ac6b662d5c72c29903813eJake Slack        private final WebSocketClient.WebSocketFuture _future;
35703928aee4356845252ac6b662d5c72c29903813eJake Slack        private final String _key;
35803928aee4356845252ac6b662d5c72c29903813eJake Slack        private final HttpParser _parser;
35903928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _accept;
36003928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _error;
36103928aee4356845252ac6b662d5c72c29903813eJake Slack        private ByteArrayBuffer _handshake;
36203928aee4356845252ac6b662d5c72c29903813eJake Slack
36303928aee4356845252ac6b662d5c72c29903813eJake Slack        public HandshakeConnection(AsyncEndPoint endpoint, WebSocketClient.WebSocketFuture future)
36403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
36503928aee4356845252ac6b662d5c72c29903813eJake Slack            super(endpoint, System.currentTimeMillis());
36603928aee4356845252ac6b662d5c72c29903813eJake Slack            _endp = endpoint;
36703928aee4356845252ac6b662d5c72c29903813eJake Slack            _future = future;
36803928aee4356845252ac6b662d5c72c29903813eJake Slack
36903928aee4356845252ac6b662d5c72c29903813eJake Slack            byte[] bytes = new byte[16];
37003928aee4356845252ac6b662d5c72c29903813eJake Slack            new Random().nextBytes(bytes);
37103928aee4356845252ac6b662d5c72c29903813eJake Slack            _key = new String(B64Code.encode(bytes));
37203928aee4356845252ac6b662d5c72c29903813eJake Slack
37303928aee4356845252ac6b662d5c72c29903813eJake Slack            Buffers buffers = new SimpleBuffers(_buffers.getBuffer(), null);
37403928aee4356845252ac6b662d5c72c29903813eJake Slack            _parser = new HttpParser(buffers, _endp, new HttpParser.EventHandler()
37503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
37603928aee4356845252ac6b662d5c72c29903813eJake Slack                @Override
37703928aee4356845252ac6b662d5c72c29903813eJake Slack                public void startResponse(Buffer version, int status, Buffer reason) throws IOException
37803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
37903928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (status != 101)
38003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
38103928aee4356845252ac6b662d5c72c29903813eJake Slack                        _error = "Bad response status " + status + " " + reason;
38203928aee4356845252ac6b662d5c72c29903813eJake Slack                        _endp.close();
38303928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
38403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
38503928aee4356845252ac6b662d5c72c29903813eJake Slack
38603928aee4356845252ac6b662d5c72c29903813eJake Slack                @Override
38703928aee4356845252ac6b662d5c72c29903813eJake Slack                public void parsedHeader(Buffer name, Buffer value) throws IOException
38803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
38903928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (__ACCEPT.equals(name))
39003928aee4356845252ac6b662d5c72c29903813eJake Slack                        _accept = value.toString();
39103928aee4356845252ac6b662d5c72c29903813eJake Slack                }
39203928aee4356845252ac6b662d5c72c29903813eJake Slack
39303928aee4356845252ac6b662d5c72c29903813eJake Slack                @Override // TODO simone says shouldn't be needed
39403928aee4356845252ac6b662d5c72c29903813eJake Slack                public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException
39503928aee4356845252ac6b662d5c72c29903813eJake Slack                {
39603928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (_error == null)
39703928aee4356845252ac6b662d5c72c29903813eJake Slack                        _error = "Bad response: " + method + " " + url + " " + version;
39803928aee4356845252ac6b662d5c72c29903813eJake Slack                    _endp.close();
39903928aee4356845252ac6b662d5c72c29903813eJake Slack                }
40003928aee4356845252ac6b662d5c72c29903813eJake Slack
40103928aee4356845252ac6b662d5c72c29903813eJake Slack                @Override // TODO simone says shouldn't be needed
40203928aee4356845252ac6b662d5c72c29903813eJake Slack                public void content(Buffer ref) throws IOException
40303928aee4356845252ac6b662d5c72c29903813eJake Slack                {
40403928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (_error == null)
40503928aee4356845252ac6b662d5c72c29903813eJake Slack                        _error = "Bad response. " + ref.length() + "B of content?";
40603928aee4356845252ac6b662d5c72c29903813eJake Slack                    _endp.close();
40703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
40803928aee4356845252ac6b662d5c72c29903813eJake Slack            });
40903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
41003928aee4356845252ac6b662d5c72c29903813eJake Slack
41103928aee4356845252ac6b662d5c72c29903813eJake Slack        private boolean handshake()
41203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
41303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_handshake==null)
41403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
41503928aee4356845252ac6b662d5c72c29903813eJake Slack                String path = _future.getURI().getPath();
41603928aee4356845252ac6b662d5c72c29903813eJake Slack                if (path == null || path.length() == 0)
41703928aee4356845252ac6b662d5c72c29903813eJake Slack                    path = "/";
41803928aee4356845252ac6b662d5c72c29903813eJake Slack
41903928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_future.getURI().getRawQuery() != null)
42003928aee4356845252ac6b662d5c72c29903813eJake Slack                    path += "?" + _future.getURI().getRawQuery();
42103928aee4356845252ac6b662d5c72c29903813eJake Slack
42203928aee4356845252ac6b662d5c72c29903813eJake Slack                String origin = _future.getOrigin();
42303928aee4356845252ac6b662d5c72c29903813eJake Slack
42403928aee4356845252ac6b662d5c72c29903813eJake Slack                StringBuilder request = new StringBuilder(512);
42503928aee4356845252ac6b662d5c72c29903813eJake Slack                request.append("GET ").append(path).append(" HTTP/1.1\r\n")
42603928aee4356845252ac6b662d5c72c29903813eJake Slack                .append("Host: ").append(_future.getURI().getHost()).append(":")
42703928aee4356845252ac6b662d5c72c29903813eJake Slack                .append(_future.getURI().getPort()).append("\r\n")
42803928aee4356845252ac6b662d5c72c29903813eJake Slack                .append("Upgrade: websocket\r\n")
42903928aee4356845252ac6b662d5c72c29903813eJake Slack                .append("Connection: Upgrade\r\n")
43003928aee4356845252ac6b662d5c72c29903813eJake Slack                .append("Sec-WebSocket-Key: ")
43103928aee4356845252ac6b662d5c72c29903813eJake Slack                .append(_key).append("\r\n");
43203928aee4356845252ac6b662d5c72c29903813eJake Slack
43303928aee4356845252ac6b662d5c72c29903813eJake Slack                if (origin != null)
43403928aee4356845252ac6b662d5c72c29903813eJake Slack                    request.append("Origin: ").append(origin).append("\r\n");
43503928aee4356845252ac6b662d5c72c29903813eJake Slack
43603928aee4356845252ac6b662d5c72c29903813eJake Slack                request.append("Sec-WebSocket-Version: ").append(WebSocketConnectionRFC6455.VERSION).append("\r\n");
43703928aee4356845252ac6b662d5c72c29903813eJake Slack
43803928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_future.getProtocol() != null)
43903928aee4356845252ac6b662d5c72c29903813eJake Slack                    request.append("Sec-WebSocket-Protocol: ").append(_future.getProtocol()).append("\r\n");
44003928aee4356845252ac6b662d5c72c29903813eJake Slack
44103928aee4356845252ac6b662d5c72c29903813eJake Slack                Map<String, String> cookies = _future.getCookies();
44203928aee4356845252ac6b662d5c72c29903813eJake Slack                if (cookies != null && cookies.size() > 0)
44303928aee4356845252ac6b662d5c72c29903813eJake Slack                {
44403928aee4356845252ac6b662d5c72c29903813eJake Slack                    for (String cookie : cookies.keySet())
44503928aee4356845252ac6b662d5c72c29903813eJake Slack                        request.append("Cookie: ")
44603928aee4356845252ac6b662d5c72c29903813eJake Slack                        .append(QuotedStringTokenizer.quoteIfNeeded(cookie, HttpFields.__COOKIE_DELIM))
44703928aee4356845252ac6b662d5c72c29903813eJake Slack                        .append("=")
44803928aee4356845252ac6b662d5c72c29903813eJake Slack                        .append(QuotedStringTokenizer.quoteIfNeeded(cookies.get(cookie), HttpFields.__COOKIE_DELIM))
44903928aee4356845252ac6b662d5c72c29903813eJake Slack                        .append("\r\n");
45003928aee4356845252ac6b662d5c72c29903813eJake Slack                }
45103928aee4356845252ac6b662d5c72c29903813eJake Slack
45203928aee4356845252ac6b662d5c72c29903813eJake Slack                request.append("\r\n");
45303928aee4356845252ac6b662d5c72c29903813eJake Slack
45403928aee4356845252ac6b662d5c72c29903813eJake Slack                _handshake=new ByteArrayBuffer(request.toString(), false);
45503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
45603928aee4356845252ac6b662d5c72c29903813eJake Slack
45703928aee4356845252ac6b662d5c72c29903813eJake Slack            // TODO extensions
45803928aee4356845252ac6b662d5c72c29903813eJake Slack
45903928aee4356845252ac6b662d5c72c29903813eJake Slack            try
46003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
46103928aee4356845252ac6b662d5c72c29903813eJake Slack                int len = _handshake.length();
46203928aee4356845252ac6b662d5c72c29903813eJake Slack                int flushed = _endp.flush(_handshake);
46303928aee4356845252ac6b662d5c72c29903813eJake Slack                if (flushed<0)
46403928aee4356845252ac6b662d5c72c29903813eJake Slack                    throw new IOException("incomplete handshake");
46503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
46603928aee4356845252ac6b662d5c72c29903813eJake Slack            catch (IOException e)
46703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
46803928aee4356845252ac6b662d5c72c29903813eJake Slack                _future.handshakeFailed(e);
46903928aee4356845252ac6b662d5c72c29903813eJake Slack            }
47003928aee4356845252ac6b662d5c72c29903813eJake Slack            return _handshake.length()==0;
47103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
47203928aee4356845252ac6b662d5c72c29903813eJake Slack
47303928aee4356845252ac6b662d5c72c29903813eJake Slack        public Connection handle() throws IOException
47403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
47503928aee4356845252ac6b662d5c72c29903813eJake Slack            while (_endp.isOpen() && !_parser.isComplete())
47603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
47703928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_handshake==null || _handshake.length()>0)
47803928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (!handshake())
47903928aee4356845252ac6b662d5c72c29903813eJake Slack                        return this;
48003928aee4356845252ac6b662d5c72c29903813eJake Slack
48103928aee4356845252ac6b662d5c72c29903813eJake Slack                if (!_parser.parseAvailable())
48203928aee4356845252ac6b662d5c72c29903813eJake Slack                {
48303928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (_endp.isInputShutdown())
48403928aee4356845252ac6b662d5c72c29903813eJake Slack                        _future.handshakeFailed(new IOException("Incomplete handshake response"));
48503928aee4356845252ac6b662d5c72c29903813eJake Slack                    return this;
48603928aee4356845252ac6b662d5c72c29903813eJake Slack                }
48703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
48803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_error == null)
48903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
49003928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_accept == null)
49103928aee4356845252ac6b662d5c72c29903813eJake Slack                {
49203928aee4356845252ac6b662d5c72c29903813eJake Slack                    _error = "No Sec-WebSocket-Accept";
49303928aee4356845252ac6b662d5c72c29903813eJake Slack                }
49403928aee4356845252ac6b662d5c72c29903813eJake Slack                else if (!WebSocketConnectionRFC6455.hashKey(_key).equals(_accept))
49503928aee4356845252ac6b662d5c72c29903813eJake Slack                {
49603928aee4356845252ac6b662d5c72c29903813eJake Slack                    _error = "Bad Sec-WebSocket-Accept";
49703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
49803928aee4356845252ac6b662d5c72c29903813eJake Slack                else
49903928aee4356845252ac6b662d5c72c29903813eJake Slack                {
50003928aee4356845252ac6b662d5c72c29903813eJake Slack                    WebSocketConnection connection = newWebSocketConnection();
50103928aee4356845252ac6b662d5c72c29903813eJake Slack
50203928aee4356845252ac6b662d5c72c29903813eJake Slack                    Buffer header = _parser.getHeaderBuffer();
50303928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (header.hasContent())
50403928aee4356845252ac6b662d5c72c29903813eJake Slack                        connection.fillBuffersFrom(header);
50503928aee4356845252ac6b662d5c72c29903813eJake Slack                    _buffers.returnBuffer(header);
50603928aee4356845252ac6b662d5c72c29903813eJake Slack
50703928aee4356845252ac6b662d5c72c29903813eJake Slack                    _future.onConnection(connection);
50803928aee4356845252ac6b662d5c72c29903813eJake Slack
50903928aee4356845252ac6b662d5c72c29903813eJake Slack                    return connection;
51003928aee4356845252ac6b662d5c72c29903813eJake Slack                }
51103928aee4356845252ac6b662d5c72c29903813eJake Slack            }
51203928aee4356845252ac6b662d5c72c29903813eJake Slack
51303928aee4356845252ac6b662d5c72c29903813eJake Slack            _endp.close();
51403928aee4356845252ac6b662d5c72c29903813eJake Slack            return this;
51503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
51603928aee4356845252ac6b662d5c72c29903813eJake Slack
51703928aee4356845252ac6b662d5c72c29903813eJake Slack        private WebSocketConnection newWebSocketConnection() throws IOException
51803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
51903928aee4356845252ac6b662d5c72c29903813eJake Slack            __log.debug("newWebSocketConnection()");
52003928aee4356845252ac6b662d5c72c29903813eJake Slack            return new WebSocketClientConnection(
52103928aee4356845252ac6b662d5c72c29903813eJake Slack                    _future._client.getFactory(),
52203928aee4356845252ac6b662d5c72c29903813eJake Slack                    _future.getWebSocket(),
52303928aee4356845252ac6b662d5c72c29903813eJake Slack                    _endp,
52403928aee4356845252ac6b662d5c72c29903813eJake Slack                    _buffers,
52503928aee4356845252ac6b662d5c72c29903813eJake Slack                    System.currentTimeMillis(),
52603928aee4356845252ac6b662d5c72c29903813eJake Slack                    _future.getMaxIdleTime(),
52703928aee4356845252ac6b662d5c72c29903813eJake Slack                    _future.getProtocol(),
52803928aee4356845252ac6b662d5c72c29903813eJake Slack                    null,
52903928aee4356845252ac6b662d5c72c29903813eJake Slack                    WebSocketConnectionRFC6455.VERSION,
53003928aee4356845252ac6b662d5c72c29903813eJake Slack                    _future.getMaskGen());
53103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
53203928aee4356845252ac6b662d5c72c29903813eJake Slack
53303928aee4356845252ac6b662d5c72c29903813eJake Slack        public void onInputShutdown() throws IOException
53403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
53503928aee4356845252ac6b662d5c72c29903813eJake Slack            _endp.close();
53603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
53703928aee4356845252ac6b662d5c72c29903813eJake Slack
53803928aee4356845252ac6b662d5c72c29903813eJake Slack        public boolean isIdle()
53903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
54003928aee4356845252ac6b662d5c72c29903813eJake Slack            return false;
54103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
54203928aee4356845252ac6b662d5c72c29903813eJake Slack
54303928aee4356845252ac6b662d5c72c29903813eJake Slack        public boolean isSuspended()
54403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
54503928aee4356845252ac6b662d5c72c29903813eJake Slack            return false;
54603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
54703928aee4356845252ac6b662d5c72c29903813eJake Slack
54803928aee4356845252ac6b662d5c72c29903813eJake Slack        public void onClose()
54903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
55003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_error != null)
55103928aee4356845252ac6b662d5c72c29903813eJake Slack                _future.handshakeFailed(new ProtocolException(_error));
55203928aee4356845252ac6b662d5c72c29903813eJake Slack            else
55303928aee4356845252ac6b662d5c72c29903813eJake Slack                _future.handshakeFailed(new EOFException());
55403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
55503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
55603928aee4356845252ac6b662d5c72c29903813eJake Slack
55703928aee4356845252ac6b662d5c72c29903813eJake Slack    private static class WebSocketClientConnection extends WebSocketConnectionRFC6455
55803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
55903928aee4356845252ac6b662d5c72c29903813eJake Slack        private final WebSocketClientFactory factory;
56003928aee4356845252ac6b662d5c72c29903813eJake Slack
56103928aee4356845252ac6b662d5c72c29903813eJake Slack        public WebSocketClientConnection(WebSocketClientFactory factory, WebSocket webSocket, EndPoint endPoint, WebSocketBuffers buffers, long timeStamp, int maxIdleTime, String protocol, List<Extension> extensions, int draftVersion, MaskGen maskGen) throws IOException
56203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
56303928aee4356845252ac6b662d5c72c29903813eJake Slack            super(webSocket, endPoint, buffers, timeStamp, maxIdleTime, protocol, extensions, draftVersion, maskGen);
56403928aee4356845252ac6b662d5c72c29903813eJake Slack            this.factory = factory;
56503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
56603928aee4356845252ac6b662d5c72c29903813eJake Slack
56703928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
56803928aee4356845252ac6b662d5c72c29903813eJake Slack        public void onClose()
56903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
57003928aee4356845252ac6b662d5c72c29903813eJake Slack            super.onClose();
57103928aee4356845252ac6b662d5c72c29903813eJake Slack            factory.removeConnection(this);
57203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
57303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
57403928aee4356845252ac6b662d5c72c29903813eJake Slack}
575