From 349d186058436ce94c7d69ca56dfcca42f3d2d2c Mon Sep 17 00:00:00 2001
From: 何书鹏 <heshupeng@bronet.cn>
Date: Mon, 21 Feb 2022 17:52:38 +0800
Subject: [PATCH] 安装workerman/phpsocket.io

---
 composer.json                                                                      |    3 ++-
 vendor/autoload.php                                                                |    2 +-
 vendor/composer/ClassLoader.php                                                    |   50 ++++++++++++++++++++++++++++++++++++++++----------
 vendor/composer/LICENSE                                                            |    2 +-
 vendor/composer/autoload_files.php                                                 |   40 ++++++++++++++++++++--------------------
 vendor/composer/autoload_psr4.php                                                  |    5 ++++-
 vendor/composer/autoload_real.php                                                  |    5 ++++-
 vendor/composer/autoload_static.php                                                |   64 +++++++++++++++++++++++++++++++++++++++++-----------------------
 vendor/composer/installed.json                                                     | 2460 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 vendor/workerman/channel/README.md                                                 |  101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/channel/composer.json                                             |   12 ++++++++++++
 vendor/workerman/channel/src/Client.php                                            |  391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/channel/src/Queue.php                                             |   89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/channel/src/Server.php                                            |  163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/channel/test/queue.php                                            |   53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/channel/test/server.php                                           |   28 ++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/.github/FUNDING.yml                                  |    4 ++++
 vendor/workerman/phpsocket.io/.gitignore                                           |    6 ++++++
 vendor/workerman/phpsocket.io/README.md                                            |  162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/composer.json                                        |   14 ++++++++++++++
 vendor/workerman/phpsocket.io/docs/README.md                                       |    3 +++
 vendor/workerman/phpsocket.io/docs/zh/README.md                                    |  259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/README.md                              |   20 ++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/index.html                      |   28 ++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/jquery.min.js                   |    4 ++++
 vendor/workerman/phpsocket.io/examples/chat/public/main.js                         |  282 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/LICENSE        |   22 ++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/index.js   |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/manager.js |  539 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/on.js      |   24 ++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/socket.js  |  411 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/url.js     |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/socket.io.js   | 7006 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/public/style.css                       |  149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/start.php                              |   12 ++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/start_io.php                           |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/examples/chat/start_web.php                          |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/ChannelAdapter.php                               |  124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Client.php                                       |  260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Debug.php                                        |   12 ++++++++++++
 vendor/workerman/phpsocket.io/src/DefaultAdapter.php                               |  106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Engine.php                                |  309 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Parser.php                                |  303 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Request.php                |   51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Response.php               |  206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Protocols/SocketIO.php                    |  209 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket.php                   |   86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket/RFC6455.php           |  335 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Socket.php                                |  380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Transport.php                             |   79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Transports/Polling.php                    |  208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Transports/PollingJsonp.php               |   63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Transports/PollingXHR.php                 |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Engine/Transports/WebSocket.php                  |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Event/Emitter.php                                |  107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Nsp.php                                          |  163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Parser/Decoder.php                               |  140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Parser/Encoder.php                               |   76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Parser/Parser.php                                |   63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/Socket.php                                       |  472 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/SocketIO.php                                     |  171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/phpsocket.io/src/autoload.php                                     |   15 +++++++++++++++
 vendor/workerman/phpsocket.io/tests/emitter.php                                    |   28 ++++++++++++++++++++++++++++
 vendor/workerman/workerman/.github/FUNDING.yml                                     |    4 ++++
 vendor/workerman/workerman/.gitignore                                              |    6 ++++++
 vendor/workerman/workerman/Autoloader.php                                          |   69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Connection/AsyncTcpConnection.php                       |  376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Connection/AsyncUdpConnection.php                       |  203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Connection/ConnectionInterface.php                      |  125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Connection/TcpConnection.php                            |  974 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Connection/UdpConnection.php                            |  208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/Ev.php                                           |  191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/Event.php                                        |  215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/EventInterface.php                               |  107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/Libevent.php                                     |  225 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/React/Base.php                                   |  264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/React/ExtEventLoop.php                           |   27 +++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/React/ExtLibEventLoop.php                        |   27 +++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/React/StreamSelectLoop.php                       |   26 ++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/Select.php                                       |  341 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Events/Swoole.php                                       |  230 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Lib/Constants.php                                       |   44 ++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Lib/Timer.php                                           |   22 ++++++++++++++++++++++
 vendor/workerman/workerman/MIT-LICENSE.txt                                         |   21 +++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Frame.php                                     |   61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http.php                                      |  330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/Chunk.php                                |   48 ++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/Request.php                              |  665 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/Response.php                             |  458 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php                     |   64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/Session.php                              |  370 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php           |  177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php          |  127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php      |  114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Http/mime.types                               |   90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/ProtocolInterface.php                         |   52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Text.php                                      |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Websocket.php                                 |  492 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Protocols/Ws.php                                        |  460 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/README.md                                               |  310 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Timer.php                                               |  213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/Worker.php                                              | 2575 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/workerman/workerman/composer.json                                           |   38 ++++++++++++++++++++++++++++++++++++++
 103 files changed, 26015 insertions(+), 1211 deletions(-)
 create mode 100644 vendor/workerman/channel/README.md
 create mode 100644 vendor/workerman/channel/composer.json
 create mode 100644 vendor/workerman/channel/src/Client.php
 create mode 100644 vendor/workerman/channel/src/Queue.php
 create mode 100644 vendor/workerman/channel/src/Server.php
 create mode 100644 vendor/workerman/channel/test/queue.php
 create mode 100644 vendor/workerman/channel/test/server.php
 create mode 100644 vendor/workerman/phpsocket.io/.github/FUNDING.yml
 create mode 100644 vendor/workerman/phpsocket.io/.gitignore
 create mode 100644 vendor/workerman/phpsocket.io/README.md
 create mode 100644 vendor/workerman/phpsocket.io/composer.json
 create mode 100644 vendor/workerman/phpsocket.io/docs/README.md
 create mode 100644 vendor/workerman/phpsocket.io/docs/zh/README.md
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/README.md
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/index.html
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/jquery.min.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/main.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/LICENSE
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/index.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/manager.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/on.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/socket.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/url.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/socket.io.js
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/public/style.css
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/start.php
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/start_io.php
 create mode 100644 vendor/workerman/phpsocket.io/examples/chat/start_web.php
 create mode 100644 vendor/workerman/phpsocket.io/src/ChannelAdapter.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Client.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Debug.php
 create mode 100644 vendor/workerman/phpsocket.io/src/DefaultAdapter.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Engine.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Parser.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Request.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Response.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Protocols/SocketIO.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket/RFC6455.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Socket.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Transport.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Transports/Polling.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Transports/PollingJsonp.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Transports/PollingXHR.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Engine/Transports/WebSocket.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Event/Emitter.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Nsp.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Parser/Decoder.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Parser/Encoder.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Parser/Parser.php
 create mode 100644 vendor/workerman/phpsocket.io/src/Socket.php
 create mode 100644 vendor/workerman/phpsocket.io/src/SocketIO.php
 create mode 100644 vendor/workerman/phpsocket.io/src/autoload.php
 create mode 100644 vendor/workerman/phpsocket.io/tests/emitter.php
 create mode 100644 vendor/workerman/workerman/.github/FUNDING.yml
 create mode 100644 vendor/workerman/workerman/.gitignore
 create mode 100644 vendor/workerman/workerman/Autoloader.php
 create mode 100644 vendor/workerman/workerman/Connection/AsyncTcpConnection.php
 create mode 100644 vendor/workerman/workerman/Connection/AsyncUdpConnection.php
 create mode 100644 vendor/workerman/workerman/Connection/ConnectionInterface.php
 create mode 100644 vendor/workerman/workerman/Connection/TcpConnection.php
 create mode 100644 vendor/workerman/workerman/Connection/UdpConnection.php
 create mode 100644 vendor/workerman/workerman/Events/Ev.php
 create mode 100644 vendor/workerman/workerman/Events/Event.php
 create mode 100644 vendor/workerman/workerman/Events/EventInterface.php
 create mode 100644 vendor/workerman/workerman/Events/Libevent.php
 create mode 100644 vendor/workerman/workerman/Events/React/Base.php
 create mode 100644 vendor/workerman/workerman/Events/React/ExtEventLoop.php
 create mode 100644 vendor/workerman/workerman/Events/React/ExtLibEventLoop.php
 create mode 100644 vendor/workerman/workerman/Events/React/StreamSelectLoop.php
 create mode 100644 vendor/workerman/workerman/Events/Select.php
 create mode 100644 vendor/workerman/workerman/Events/Swoole.php
 create mode 100644 vendor/workerman/workerman/Lib/Constants.php
 create mode 100644 vendor/workerman/workerman/Lib/Timer.php
 create mode 100644 vendor/workerman/workerman/MIT-LICENSE.txt
 create mode 100644 vendor/workerman/workerman/Protocols/Frame.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/Chunk.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/Request.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/Response.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/Session.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php
 create mode 100644 vendor/workerman/workerman/Protocols/Http/mime.types
 create mode 100644 vendor/workerman/workerman/Protocols/ProtocolInterface.php
 create mode 100644 vendor/workerman/workerman/Protocols/Text.php
 create mode 100644 vendor/workerman/workerman/Protocols/Websocket.php
 create mode 100644 vendor/workerman/workerman/Protocols/Ws.php
 create mode 100644 vendor/workerman/workerman/README.md
 create mode 100644 vendor/workerman/workerman/Timer.php
 create mode 100644 vendor/workerman/workerman/Worker.php
 create mode 100644 vendor/workerman/workerman/composer.json

diff --git a/composer.json b/composer.json
index 1de5563..fa5e00d 100644
--- a/composer.json
+++ b/composer.json
@@ -30,7 +30,8 @@
         "ext-curl": "*",
         "ext-pdo": "*",
         "ext-bcmath": "*",
-        "txthinking/mailer": "^2.0"
+        "txthinking/mailer": "^2.0",
+        "workerman/phpsocket.io": "^1.1"
     },
     "config": {
         "preferred-install": "dist"
diff --git a/vendor/autoload.php b/vendor/autoload.php
index 7fca814..08f044c 100644
--- a/vendor/autoload.php
+++ b/vendor/autoload.php
@@ -2,6 +2,6 @@
 
 // autoload.php @generated by Composer
 
-require_once __DIR__ . '/composer' . '/autoload_real.php';
+require_once __DIR__ . '/composer/autoload_real.php';
 
 return ComposerAutoloaderInit73f9e72fede2c36621e52f7b610bbb65::getLoader();
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
index ac67d30..03b9bb9 100644
--- a/vendor/composer/ClassLoader.php
+++ b/vendor/composer/ClassLoader.php
@@ -55,11 +55,12 @@ class ClassLoader
     private $classMap = array();
     private $classMapAuthoritative = false;
     private $missingClasses = array();
+    private $apcuPrefix;
 
     public function getPrefixes()
     {
         if (!empty($this->prefixesPsr0)) {
-            return call_user_func_array('array_merge', $this->prefixesPsr0);
+            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
         }
 
         return array();
@@ -272,6 +273,26 @@ class ClassLoader
     }
 
     /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
      * Registers this instance as an autoloader.
      *
      * @param bool $prepend Whether to prepend the autoloader or not
@@ -313,11 +334,6 @@ class ClassLoader
      */
     public function findFile($class)
     {
-        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
-        if ('\\' == $class[0]) {
-            $class = substr($class, 1);
-        }
-
         // class map lookup
         if (isset($this->classMap[$class])) {
             return $this->classMap[$class];
@@ -325,6 +341,12 @@ class ClassLoader
         if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
             return false;
         }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
 
         $file = $this->findFileWithExtension($class, '.php');
 
@@ -333,6 +355,10 @@ class ClassLoader
             $file = $this->findFileWithExtension($class, '.hh');
         }
 
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
         if (false === $file) {
             // Remember that this class does not exist.
             $this->missingClasses[$class] = true;
@@ -348,10 +374,14 @@ class ClassLoader
 
         $first = $class[0];
         if (isset($this->prefixLengthsPsr4[$first])) {
-            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
-                if (0 === strpos($class, $prefix)) {
-                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
-                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath . '\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
                             return $file;
                         }
                     }
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
index 1a28124..f27399a 100644
--- a/vendor/composer/LICENSE
+++ b/vendor/composer/LICENSE
@@ -1,5 +1,5 @@
 
-Copyright (c) 2016 Nils Adermann, Jordi Boggiano
+Copyright (c) Nils Adermann, Jordi Boggiano
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
index 9e82bd3..4b27921 100644
--- a/vendor/composer/autoload_files.php
+++ b/vendor/composer/autoload_files.php
@@ -7,31 +7,14 @@ $baseDir = dirname($vendorDir);
 
 return array(
     '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
-    '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
     'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+    '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
     'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
     'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
     '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
     'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
     'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
     '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
-    '9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
-    '3af723442581d6c310bf44543f9f5c60' => $vendorDir . '/markbaker/matrix/classes/src/Functions/adjoint.php',
-    'd803221834c8b57fec95debb5406a797' => $vendorDir . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
-    '4714cafbd3be4c72c274a25eae9396bb' => $vendorDir . '/markbaker/matrix/classes/src/Functions/cofactors.php',
-    '89719dc7c77436609d1c1c31f0797b8f' => $vendorDir . '/markbaker/matrix/classes/src/Functions/determinant.php',
-    'c28af79ec7730859d83f2d4310b8dd0b' => $vendorDir . '/markbaker/matrix/classes/src/Functions/diagonal.php',
-    'c5d82bf1ac485e445f911e55789ab4e6' => $vendorDir . '/markbaker/matrix/classes/src/Functions/identity.php',
-    '0d2d594de24a247f7a33499e933aa21e' => $vendorDir . '/markbaker/matrix/classes/src/Functions/inverse.php',
-    'f37c25880804a014ef40c8bffbab1b10' => $vendorDir . '/markbaker/matrix/classes/src/Functions/minors.php',
-    'd6e4e42171df0dbea253b3067fefda38' => $vendorDir . '/markbaker/matrix/classes/src/Functions/trace.php',
-    '2c9b19fa954fd3e6fcc7e7a1383caddd' => $vendorDir . '/markbaker/matrix/classes/src/Functions/transpose.php',
-    '0a538fc9b897450ec362480ebbebe94f' => $vendorDir . '/markbaker/matrix/classes/src/Operations/add.php',
-    'f0843f7f4089ec2343c7445544356385' => $vendorDir . '/markbaker/matrix/classes/src/Operations/directsum.php',
-    'ad3e8c29aa16d134661a414265677b61' => $vendorDir . '/markbaker/matrix/classes/src/Operations/subtract.php',
-    '8d37dad4703fab45bfec9dd0bbf3278e' => $vendorDir . '/markbaker/matrix/classes/src/Operations/multiply.php',
-    '4888a6f58c08148ebe17682f9ce9b2a8' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideby.php',
-    'eef6fa3879d3efa347cd24d5eb348f85' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideinto.php',
     'abede361264e2ae69ec1eee813a101af' => $vendorDir . '/markbaker/complex/classes/src/functions/abs.php',
     '21a5860fbef5be28db5ddfbc3cca67c4' => $vendorDir . '/markbaker/complex/classes/src/functions/acos.php',
     '1546e3f9d127f2a9bb2d1b6c31c26ef1' => $vendorDir . '/markbaker/complex/classes/src/functions/acosh.php',
@@ -74,9 +57,26 @@ return array(
     '883af48563631547925fa4c3b48ead07' => $vendorDir . '/markbaker/complex/classes/src/operations/multiply.php',
     'f190e3308e6ca23234a2875edc985c03' => $vendorDir . '/markbaker/complex/classes/src/operations/divideby.php',
     'ac9e33ce6841aa5bf5d16d465a2f03a7' => $vendorDir . '/markbaker/complex/classes/src/operations/divideinto.php',
-    'cc56288302d9df745d97c934d6a6e5f0' => $vendorDir . '/topthink/think-queue/src/common.php',
+    '3af723442581d6c310bf44543f9f5c60' => $vendorDir . '/markbaker/matrix/classes/src/Functions/adjoint.php',
+    'd803221834c8b57fec95debb5406a797' => $vendorDir . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
+    '4714cafbd3be4c72c274a25eae9396bb' => $vendorDir . '/markbaker/matrix/classes/src/Functions/cofactors.php',
+    '89719dc7c77436609d1c1c31f0797b8f' => $vendorDir . '/markbaker/matrix/classes/src/Functions/determinant.php',
+    'c28af79ec7730859d83f2d4310b8dd0b' => $vendorDir . '/markbaker/matrix/classes/src/Functions/diagonal.php',
+    'c5d82bf1ac485e445f911e55789ab4e6' => $vendorDir . '/markbaker/matrix/classes/src/Functions/identity.php',
+    '0d2d594de24a247f7a33499e933aa21e' => $vendorDir . '/markbaker/matrix/classes/src/Functions/inverse.php',
+    'f37c25880804a014ef40c8bffbab1b10' => $vendorDir . '/markbaker/matrix/classes/src/Functions/minors.php',
+    'd6e4e42171df0dbea253b3067fefda38' => $vendorDir . '/markbaker/matrix/classes/src/Functions/trace.php',
+    '2c9b19fa954fd3e6fcc7e7a1383caddd' => $vendorDir . '/markbaker/matrix/classes/src/Functions/transpose.php',
+    '0a538fc9b897450ec362480ebbebe94f' => $vendorDir . '/markbaker/matrix/classes/src/Operations/add.php',
+    'f0843f7f4089ec2343c7445544356385' => $vendorDir . '/markbaker/matrix/classes/src/Operations/directsum.php',
+    'ad3e8c29aa16d134661a414265677b61' => $vendorDir . '/markbaker/matrix/classes/src/Operations/subtract.php',
+    '8d37dad4703fab45bfec9dd0bbf3278e' => $vendorDir . '/markbaker/matrix/classes/src/Operations/multiply.php',
+    '4888a6f58c08148ebe17682f9ce9b2a8' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideby.php',
+    'eef6fa3879d3efa347cd24d5eb348f85' => $vendorDir . '/markbaker/matrix/classes/src/Operations/divideinto.php',
+    '9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
+    '488987c28e9b5e95a1ce6b6bcb94606c' => $vendorDir . '/karsonzhang/fastadmin-addons/src/common.php',
     'f0e7e63bbb278a92db02393536748c5f' => $vendorDir . '/overtrue/wechat/src/Kernel/Support/Helpers.php',
     '6747f579ad6817f318cc3a7e7a0abb93' => $vendorDir . '/overtrue/wechat/src/Kernel/Helpers.php',
     '1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
-    '488987c28e9b5e95a1ce6b6bcb94606c' => $vendorDir . '/karsonzhang/fastadmin-addons/src/common.php',
+    'cc56288302d9df745d97c934d6a6e5f0' => $vendorDir . '/topthink/think-queue/src/common.php',
 );
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index 4052326..61afd74 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -9,7 +9,8 @@ return array(
     'think\\helper\\' => array($vendorDir . '/topthink/think-helper/src'),
     'think\\composer\\' => array($vendorDir . '/topthink/think-installer/src'),
     'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
-    'think\\' => array($vendorDir . '/topthink/think-queue/src', $vendorDir . '/karsonzhang/fastadmin-addons/src', $baseDir . '/thinkphp/library/think'),
+    'think\\' => array($vendorDir . '/karsonzhang/fastadmin-addons/src', $baseDir . '/thinkphp/library/think', $vendorDir . '/topthink/think-queue/src'),
+    'Workerman\\' => array($vendorDir . '/workerman/workerman'),
     'Tx\\' => array($vendorDir . '/txthinking/mailer/src'),
     'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
     'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
@@ -33,6 +34,7 @@ return array(
     'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
     'PhpZip\\' => array($vendorDir . '/nelexa/zip/src'),
     'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
+    'PHPSocketIO\\' => array($vendorDir . '/workerman/phpsocket.io/src'),
     'Overtrue\\Socialite\\' => array($vendorDir . '/overtrue/socialite/src'),
     'Overtrue\\Pinyin\\' => array($vendorDir . '/overtrue/pinyin/src'),
     'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
@@ -43,4 +45,5 @@ return array(
     'EasyWeChat\\' => array($vendorDir . '/overtrue/wechat/src'),
     'EasyWeChatComposer\\' => array($vendorDir . '/easywechat-composer/easywechat-composer/src'),
     'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
+    'Channel\\' => array($vendorDir . '/workerman/channel/src'),
 );
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
index 5256752..d5fe3d8 100644
--- a/vendor/composer/autoload_real.php
+++ b/vendor/composer/autoload_real.php
@@ -13,6 +13,9 @@ class ComposerAutoloaderInit73f9e72fede2c36621e52f7b610bbb65
         }
     }
 
+    /**
+     * @return \Composer\Autoload\ClassLoader
+     */
     public static function getLoader()
     {
         if (null !== self::$loader) {
@@ -23,7 +26,7 @@ class ComposerAutoloaderInit73f9e72fede2c36621e52f7b610bbb65
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
         spl_autoload_unregister(array('ComposerAutoloaderInit73f9e72fede2c36621e52f7b610bbb65', 'loadClassLoader'));
 
-        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
         if ($useStaticLoader) {
             require_once __DIR__ . '/autoload_static.php';
 
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 6e3ddef..f72e6e7 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -8,31 +8,14 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
 {
     public static $files = array (
         '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
-        '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
         'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+        '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
         'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
         'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
         '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
         'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
         'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
         '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
-        '9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
-        '3af723442581d6c310bf44543f9f5c60' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/adjoint.php',
-        'd803221834c8b57fec95debb5406a797' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
-        '4714cafbd3be4c72c274a25eae9396bb' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/cofactors.php',
-        '89719dc7c77436609d1c1c31f0797b8f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/determinant.php',
-        'c28af79ec7730859d83f2d4310b8dd0b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/diagonal.php',
-        'c5d82bf1ac485e445f911e55789ab4e6' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/identity.php',
-        '0d2d594de24a247f7a33499e933aa21e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/inverse.php',
-        'f37c25880804a014ef40c8bffbab1b10' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/minors.php',
-        'd6e4e42171df0dbea253b3067fefda38' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/trace.php',
-        '2c9b19fa954fd3e6fcc7e7a1383caddd' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/transpose.php',
-        '0a538fc9b897450ec362480ebbebe94f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/add.php',
-        'f0843f7f4089ec2343c7445544356385' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/directsum.php',
-        'ad3e8c29aa16d134661a414265677b61' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/subtract.php',
-        '8d37dad4703fab45bfec9dd0bbf3278e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/multiply.php',
-        '4888a6f58c08148ebe17682f9ce9b2a8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideby.php',
-        'eef6fa3879d3efa347cd24d5eb348f85' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideinto.php',
         'abede361264e2ae69ec1eee813a101af' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/abs.php',
         '21a5860fbef5be28db5ddfbc3cca67c4' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acos.php',
         '1546e3f9d127f2a9bb2d1b6c31c26ef1' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acosh.php',
@@ -75,11 +58,28 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
         '883af48563631547925fa4c3b48ead07' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/multiply.php',
         'f190e3308e6ca23234a2875edc985c03' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideby.php',
         'ac9e33ce6841aa5bf5d16d465a2f03a7' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideinto.php',
-        'cc56288302d9df745d97c934d6a6e5f0' => __DIR__ . '/..' . '/topthink/think-queue/src/common.php',
+        '3af723442581d6c310bf44543f9f5c60' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/adjoint.php',
+        'd803221834c8b57fec95debb5406a797' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/antidiagonal.php',
+        '4714cafbd3be4c72c274a25eae9396bb' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/cofactors.php',
+        '89719dc7c77436609d1c1c31f0797b8f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/determinant.php',
+        'c28af79ec7730859d83f2d4310b8dd0b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/diagonal.php',
+        'c5d82bf1ac485e445f911e55789ab4e6' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/identity.php',
+        '0d2d594de24a247f7a33499e933aa21e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/inverse.php',
+        'f37c25880804a014ef40c8bffbab1b10' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/minors.php',
+        'd6e4e42171df0dbea253b3067fefda38' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/trace.php',
+        '2c9b19fa954fd3e6fcc7e7a1383caddd' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Functions/transpose.php',
+        '0a538fc9b897450ec362480ebbebe94f' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/add.php',
+        'f0843f7f4089ec2343c7445544356385' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/directsum.php',
+        'ad3e8c29aa16d134661a414265677b61' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/subtract.php',
+        '8d37dad4703fab45bfec9dd0bbf3278e' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/multiply.php',
+        '4888a6f58c08148ebe17682f9ce9b2a8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideby.php',
+        'eef6fa3879d3efa347cd24d5eb348f85' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/Operations/divideinto.php',
+        '9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
+        '488987c28e9b5e95a1ce6b6bcb94606c' => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src/common.php',
         'f0e7e63bbb278a92db02393536748c5f' => __DIR__ . '/..' . '/overtrue/wechat/src/Kernel/Support/Helpers.php',
         '6747f579ad6817f318cc3a7e7a0abb93' => __DIR__ . '/..' . '/overtrue/wechat/src/Kernel/Helpers.php',
         '1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
-        '488987c28e9b5e95a1ce6b6bcb94606c' => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src/common.php',
+        'cc56288302d9df745d97c934d6a6e5f0' => __DIR__ . '/..' . '/topthink/think-queue/src/common.php',
     );
 
     public static $prefixLengthsPsr4 = array (
@@ -90,6 +90,10 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
             'think\\captcha\\' => 14,
             'think\\' => 6,
         ),
+        'W' => 
+        array (
+            'Workerman\\' => 10,
+        ),
         'T' => 
         array (
             'Tx\\' => 3,
@@ -121,6 +125,7 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
             'Psr\\Cache\\' => 10,
             'PhpZip\\' => 7,
             'PhpOffice\\PhpSpreadsheet\\' => 25,
+            'PHPSocketIO\\' => 12,
         ),
         'O' => 
         array (
@@ -146,6 +151,7 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
         'C' => 
         array (
             'Complex\\' => 8,
+            'Channel\\' => 8,
         ),
     );
 
@@ -164,9 +170,13 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
         ),
         'think\\' => 
         array (
-            0 => __DIR__ . '/..' . '/topthink/think-queue/src',
-            1 => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src',
-            2 => __DIR__ . '/../..' . '/thinkphp/library/think',
+            0 => __DIR__ . '/..' . '/karsonzhang/fastadmin-addons/src',
+            1 => __DIR__ . '/../..' . '/thinkphp/library/think',
+            2 => __DIR__ . '/..' . '/topthink/think-queue/src',
+        ),
+        'Workerman\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/workerman',
         ),
         'Tx\\' => 
         array (
@@ -260,6 +270,10 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
         array (
             0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
         ),
+        'PHPSocketIO\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/phpsocket.io/src',
+        ),
         'Overtrue\\Socialite\\' => 
         array (
             0 => __DIR__ . '/..' . '/overtrue/socialite/src',
@@ -300,6 +314,10 @@ class ComposerStaticInit73f9e72fede2c36621e52f7b610bbb65
         array (
             0 => __DIR__ . '/..' . '/markbaker/complex/classes/src',
         ),
+        'Channel\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/workerman/channel/src',
+        ),
     );
 
     public static $prefixesPsr0 = array (
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 5030d11..7fb99b1 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1,54 +1,5 @@
 [
     {
-        "name": "topthink/think-installer",
-        "version": "v1.0.14",
-        "version_normalized": "1.0.14.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/top-think/think-installer.git",
-            "reference": "eae1740ac264a55c06134b6685dfb9f837d004d1"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/top-think/think-installer/zipball/eae1740ac264a55c06134b6685dfb9f837d004d1",
-            "reference": "eae1740ac264a55c06134b6685dfb9f837d004d1",
-            "shasum": "",
-            "mirrors": [
-                {
-                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
-                    "preferred": true
-                }
-            ]
-        },
-        "require": {
-            "composer-plugin-api": "^1.0||^2.0"
-        },
-        "require-dev": {
-            "composer/composer": "^1.0||^2.0"
-        },
-        "time": "2021-03-25 08:34:02",
-        "type": "composer-plugin",
-        "extra": {
-            "class": "think\\composer\\Plugin"
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "think\\composer\\": "src"
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "Apache-2.0"
-        ],
-        "authors": [
-            {
-                "name": "yunwuxin",
-                "email": "448901948@qq.com"
-            }
-        ]
-    },
-    {
         "name": "easywechat-composer/easywechat-composer",
         "version": "1.4.0",
         "version_normalized": "1.4.0.0",
@@ -77,7 +28,7 @@
             "composer/composer": "^1.0 || ^2.0",
             "phpunit/phpunit": "^6.5 || ^7.0"
         },
-        "time": "2020-07-23 11:06:47",
+        "time": "2020-07-23T11:06:47+00:00",
         "type": "composer-plugin",
         "extra": {
             "class": "EasyWeChatComposer\\Plugin"
@@ -101,18 +52,18 @@
         "description": "The composer plugin for EasyWeChat"
     },
     {
-        "name": "topthink/think-helper",
-        "version": "v1.0.7",
-        "version_normalized": "1.0.7.0",
+        "name": "guzzlehttp/guzzle",
+        "version": "6.5.5",
+        "version_normalized": "6.5.5.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/top-think/think-helper.git",
-            "reference": "5f92178606c8ce131d36b37a57c58eb71e55f019"
+            "url": "https://github.com/guzzle/guzzle.git",
+            "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/top-think/think-helper/zipball/5f92178606c8ce131d36b37a57c58eb71e55f019",
-            "reference": "5f92178606c8ce131d36b37a57c58eb71e55f019",
+            "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+            "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
             "shasum": "",
             "mirrors": [
                 {
@@ -121,42 +72,73 @@
                 }
             ]
         },
-        "time": "2018-10-05 00:43:21",
+        "require": {
+            "ext-json": "*",
+            "guzzlehttp/promises": "^1.0",
+            "guzzlehttp/psr7": "^1.6.1",
+            "php": ">=5.5",
+            "symfony/polyfill-intl-idn": "^1.17.0"
+        },
+        "require-dev": {
+            "ext-curl": "*",
+            "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+            "psr/log": "^1.1"
+        },
+        "suggest": {
+            "psr/log": "Required for using the Log middleware"
+        },
+        "time": "2020-06-16T21:01:06+00:00",
         "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "6.5-dev"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "think\\helper\\": "src"
+                "GuzzleHttp\\": "src/"
             },
             "files": [
-                "src/helper.php"
+                "src/functions_include.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "Apache-2.0"
+            "MIT"
         ],
         "authors": [
             {
-                "name": "yunwuxin",
-                "email": "448901948@qq.com"
+                "name": "Michael Dowling",
+                "email": "mtdowling@gmail.com",
+                "homepage": "https://github.com/mtdowling"
             }
         ],
-        "description": "The ThinkPHP5 Helper Package"
+        "description": "Guzzle is a PHP HTTP client library",
+        "homepage": "http://guzzlephp.org/",
+        "keywords": [
+            "client",
+            "curl",
+            "framework",
+            "http",
+            "http client",
+            "rest",
+            "web service"
+        ]
     },
     {
-        "name": "topthink/think-queue",
-        "version": "v1.1.6",
-        "version_normalized": "1.1.6.0",
+        "name": "guzzlehttp/promises",
+        "version": "1.4.1",
+        "version_normalized": "1.4.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/top-think/think-queue.git",
-            "reference": "250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245"
+            "url": "https://github.com/guzzle/promises.git",
+            "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/top-think/think-queue/zipball/250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245",
-            "reference": "250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245",
+            "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+            "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d",
             "shasum": "",
             "mirrors": [
                 {
@@ -166,53 +148,56 @@
             ]
         },
         "require": {
-            "topthink/think-helper": ">=1.0.4",
-            "topthink/think-installer": ">=1.0.10"
+            "php": ">=5.5"
         },
         "require-dev": {
-            "topthink/framework": "~5.0.0"
+            "symfony/phpunit-bridge": "^4.4 || ^5.1"
         },
-        "time": "2018-10-15 10:16:55",
-        "type": "think-extend",
+        "time": "2021-03-07T09:25:29+00:00",
+        "type": "library",
         "extra": {
-            "think-config": {
-                "queue": "src/config.php"
+            "branch-alias": {
+                "dev-master": "1.4-dev"
             }
         },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "think\\": "src"
+                "GuzzleHttp\\Promise\\": "src/"
             },
             "files": [
-                "src/common.php"
+                "src/functions_include.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "Apache-2.0"
+            "MIT"
         ],
         "authors": [
             {
-                "name": "yunwuxin",
-                "email": "448901948@qq.com"
+                "name": "Michael Dowling",
+                "email": "mtdowling@gmail.com",
+                "homepage": "https://github.com/mtdowling"
             }
         ],
-        "description": "The ThinkPHP5 Queue Package"
+        "description": "Guzzle promises library",
+        "keywords": [
+            "promise"
+        ]
     },
     {
-        "name": "psr/simple-cache",
-        "version": "1.0.1",
-        "version_normalized": "1.0.1.0",
+        "name": "guzzlehttp/psr7",
+        "version": "1.8.1",
+        "version_normalized": "1.8.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/php-fig/simple-cache.git",
-            "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+            "url": "https://github.com/guzzle/psr7.git",
+            "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
-            "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+            "url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1",
+            "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1",
             "shasum": "",
             "mirrors": [
                 {
@@ -222,20 +207,35 @@
             ]
         },
         "require": {
-            "php": ">=5.3.0"
+            "php": ">=5.4.0",
+            "psr/http-message": "~1.0",
+            "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+        },
+        "provide": {
+            "psr/http-message-implementation": "1.0"
+        },
+        "require-dev": {
+            "ext-zlib": "*",
+            "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+        },
+        "suggest": {
+            "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
         },
-        "time": "2017-10-23 01:57:42",
+        "time": "2021-03-21T16:25:00+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.0.x-dev"
+                "dev-master": "1.7-dev"
             }
         },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Psr\\SimpleCache\\": "src/"
-            }
+                "GuzzleHttp\\Psr7\\": "src/"
+            },
+            "files": [
+                "src/functions_include.php"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -243,32 +243,40 @@
         ],
         "authors": [
             {
-                "name": "PHP-FIG",
-                "homepage": "http://www.php-fig.org/"
+                "name": "Michael Dowling",
+                "email": "mtdowling@gmail.com",
+                "homepage": "https://github.com/mtdowling"
+            },
+            {
+                "name": "Tobias Schultze",
+                "homepage": "https://github.com/Tobion"
             }
         ],
-        "description": "Common interfaces for simple caching",
+        "description": "PSR-7 message implementation that also provides common utility methods",
         "keywords": [
-            "cache",
-            "caching",
-            "psr",
-            "psr-16",
-            "simple-cache"
+            "http",
+            "message",
+            "psr-7",
+            "request",
+            "response",
+            "stream",
+            "uri",
+            "url"
         ]
     },
     {
-        "name": "markbaker/matrix",
-        "version": "1.2.3",
-        "version_normalized": "1.2.3.0",
+        "name": "karsonzhang/fastadmin-addons",
+        "version": "1.2.10",
+        "version_normalized": "1.2.10.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/MarkBaker/PHPMatrix.git",
-            "reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d"
+            "url": "https://github.com/karsonzhang/fastadmin-addons.git",
+            "reference": "276dbed2bcaaa4eaab2019b36e4886123c2d985c"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/44bb1ab01811116f01fe216ab37d921dccc6c10d",
-            "reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d",
+            "url": "https://api.github.com/repos/karsonzhang/fastadmin-addons/zipball/276dbed2bcaaa4eaab2019b36e4886123c2d985c",
+            "reference": "276dbed2bcaaa4eaab2019b36e4886123c2d985c",
             "shasum": "",
             "mirrors": [
                 {
@@ -278,60 +286,42 @@
             ]
         },
         "require": {
-            "php": "^5.6.0|^7.0.0"
-        },
-        "require-dev": {
-            "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
-            "phpcompatibility/php-compatibility": "dev-master",
-            "phploc/phploc": "^4",
-            "phpmd/phpmd": "dev-master",
-            "phpunit/phpunit": "^5.7|^6.0|7.0",
-            "sebastian/phpcpd": "^3.0",
-            "squizlabs/php_codesniffer": "^3.0@dev"
+            "nelexa/zip": "^3.3",
+            "php": ">=7.0.0",
+            "symfony/var-exporter": "^4.4.13"
         },
-        "time": "2021-01-26 14:36:01",
+        "time": "2021-04-07T04:02:27+00:00",
         "type": "library",
+        "extra": {
+            "think-config": {
+                "addons": "src/config.php"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Matrix\\": "classes/src/"
+                "think\\": "src/"
             },
             "files": [
-                "classes/src/Functions/adjoint.php",
-                "classes/src/Functions/antidiagonal.php",
-                "classes/src/Functions/cofactors.php",
-                "classes/src/Functions/determinant.php",
-                "classes/src/Functions/diagonal.php",
-                "classes/src/Functions/identity.php",
-                "classes/src/Functions/inverse.php",
-                "classes/src/Functions/minors.php",
-                "classes/src/Functions/trace.php",
-                "classes/src/Functions/transpose.php",
-                "classes/src/Operations/add.php",
-                "classes/src/Operations/directsum.php",
-                "classes/src/Operations/subtract.php",
-                "classes/src/Operations/multiply.php",
-                "classes/src/Operations/divideby.php",
-                "classes/src/Operations/divideinto.php"
+                "src/common.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "MIT"
+            "Apache-2.0"
         ],
         "authors": [
             {
-                "name": "Mark Baker",
-                "email": "mark@lange.demon.co.uk"
+                "name": "Karson",
+                "email": "karson@fastadmin.net"
+            },
+            {
+                "name": "xiaobo.sun",
+                "email": "xiaobo.sun@qq.com"
             }
         ],
-        "description": "PHP Class for working with matrices",
-        "homepage": "https://github.com/MarkBaker/PHPMatrix",
-        "keywords": [
-            "mathematics",
-            "matrix",
-            "vector"
-        ]
+        "description": "addons package for fastadmin",
+        "homepage": "https://github.com/karsonzhang/fastadmin-addons"
     },
     {
         "name": "markbaker/complex",
@@ -367,7 +357,7 @@
             "sebastian/phpcpd": "2.*",
             "squizlabs/php_codesniffer": "^3.4.0"
         },
-        "time": "2020-08-26 19:47:57",
+        "time": "2020-08-26T19:47:57+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
@@ -437,18 +427,18 @@
         ]
     },
     {
-        "name": "phpoffice/phpspreadsheet",
-        "version": "1.12.0",
-        "version_normalized": "1.12.0.0",
+        "name": "markbaker/matrix",
+        "version": "1.2.3",
+        "version_normalized": "1.2.3.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
-            "reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd"
+            "url": "https://github.com/MarkBaker/PHPMatrix.git",
+            "reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
-            "reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
+            "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/44bb1ab01811116f01fe216ab37d921dccc6c10d",
+            "reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d",
             "shasum": "",
             "mirrors": [
                 {
@@ -458,47 +448,42 @@
             ]
         },
         "require": {
-            "ext-ctype": "*",
-            "ext-dom": "*",
-            "ext-fileinfo": "*",
-            "ext-gd": "*",
-            "ext-iconv": "*",
-            "ext-libxml": "*",
-            "ext-mbstring": "*",
-            "ext-simplexml": "*",
-            "ext-xml": "*",
-            "ext-xmlreader": "*",
-            "ext-xmlwriter": "*",
-            "ext-zip": "*",
-            "ext-zlib": "*",
-            "markbaker/complex": "^1.4",
-            "markbaker/matrix": "^1.2",
-            "php": "^7.1",
-            "psr/simple-cache": "^1.0"
+            "php": "^5.6.0|^7.0.0"
         },
         "require-dev": {
-            "dompdf/dompdf": "^0.8.3",
-            "friendsofphp/php-cs-fixer": "^2.16",
-            "jpgraph/jpgraph": "^4.0",
-            "mpdf/mpdf": "^8.0",
-            "phpcompatibility/php-compatibility": "^9.3",
-            "phpunit/phpunit": "^7.5",
-            "squizlabs/php_codesniffer": "^3.5",
-            "tecnickcom/tcpdf": "^6.3"
-        },
-        "suggest": {
-            "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
-            "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
-            "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
-            "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
+            "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+            "phpcompatibility/php-compatibility": "dev-master",
+            "phploc/phploc": "^4",
+            "phpmd/phpmd": "dev-master",
+            "phpunit/phpunit": "^5.7|^6.0|7.0",
+            "sebastian/phpcpd": "^3.0",
+            "squizlabs/php_codesniffer": "^3.0@dev"
         },
-        "time": "2020-04-27 08:12:48",
+        "time": "2021-01-26T14:36:01+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
-            }
+                "Matrix\\": "classes/src/"
+            },
+            "files": [
+                "classes/src/Functions/adjoint.php",
+                "classes/src/Functions/antidiagonal.php",
+                "classes/src/Functions/cofactors.php",
+                "classes/src/Functions/determinant.php",
+                "classes/src/Functions/diagonal.php",
+                "classes/src/Functions/identity.php",
+                "classes/src/Functions/inverse.php",
+                "classes/src/Functions/minors.php",
+                "classes/src/Functions/trace.php",
+                "classes/src/Functions/transpose.php",
+                "classes/src/Operations/add.php",
+                "classes/src/Operations/directsum.php",
+                "classes/src/Operations/subtract.php",
+                "classes/src/Operations/multiply.php",
+                "classes/src/Operations/divideby.php",
+                "classes/src/Operations/divideinto.php"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -506,50 +491,31 @@
         ],
         "authors": [
             {
-                "name": "Maarten Balliauw",
-                "homepage": "https://blog.maartenballiauw.be"
-            },
-            {
                 "name": "Mark Baker",
-                "homepage": "https://markbakeruk.net"
-            },
-            {
-                "name": "Franck Lefevre",
-                "homepage": "https://rootslabs.net"
-            },
-            {
-                "name": "Erik Tilt"
-            },
-            {
-                "name": "Adrien Crivelli"
+                "email": "mark@lange.demon.co.uk"
             }
         ],
-        "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
-        "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
+        "description": "PHP Class for working with matrices",
+        "homepage": "https://github.com/MarkBaker/PHPMatrix",
         "keywords": [
-            "OpenXML",
-            "excel",
-            "gnumeric",
-            "ods",
-            "php",
-            "spreadsheet",
-            "xls",
-            "xlsx"
+            "mathematics",
+            "matrix",
+            "vector"
         ]
     },
     {
-        "name": "symfony/polyfill-php80",
-        "version": "v1.22.1",
-        "version_normalized": "1.22.1.0",
+        "name": "monolog/monolog",
+        "version": "1.26.0",
+        "version_normalized": "1.26.0.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/polyfill-php80.git",
-            "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+            "url": "https://github.com/Seldaek/monolog.git",
+            "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
-            "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+            "url": "https://api.github.com/repos/Seldaek/monolog/zipball/2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
+            "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
             "shasum": "",
             "mirrors": [
                 {
@@ -559,30 +525,44 @@
             ]
         },
         "require": {
-            "php": ">=7.1"
+            "php": ">=5.3.0",
+            "psr/log": "~1.0"
         },
-        "time": "2021-01-07 16:49:33",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-main": "1.22-dev"
-            },
-            "thanks": {
-                "name": "symfony/polyfill",
-                "url": "https://github.com/symfony/polyfill"
-            }
+        "provide": {
+            "psr/log-implementation": "1.0.0"
+        },
+        "require-dev": {
+            "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+            "doctrine/couchdb": "~1.0@dev",
+            "graylog2/gelf-php": "~1.0",
+            "php-amqplib/php-amqplib": "~2.4",
+            "php-console/php-console": "^3.1.3",
+            "phpstan/phpstan": "^0.12.59",
+            "phpunit/phpunit": "~4.5",
+            "ruflin/elastica": ">=0.90 <3.0",
+            "sentry/sentry": "^0.13",
+            "swiftmailer/swiftmailer": "^5.3|^6.0"
+        },
+        "suggest": {
+            "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+            "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+            "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+            "ext-mongo": "Allow sending log messages to a MongoDB server",
+            "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+            "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
+            "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+            "php-console/php-console": "Allow sending log messages to Google Chrome",
+            "rollbar/rollbar": "Allow sending log messages to Rollbar",
+            "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
+            "sentry/sentry": "Allow sending log messages to a Sentry server"
         },
+        "time": "2020-12-14T12:56:38+00:00",
+        "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Polyfill\\Php80\\": ""
-            },
-            "files": [
-                "bootstrap.php"
-            ],
-            "classmap": [
-                "Resources/stubs"
-            ]
+                "Monolog\\": "src/Monolog"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -590,40 +570,32 @@
         ],
         "authors": [
             {
-                "name": "Ion Bazan",
-                "email": "ion.bazan@gmail.com"
-            },
-            {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "Jordi Boggiano",
+                "email": "j.boggiano@seld.be",
+                "homepage": "http://seld.be"
             }
         ],
-        "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
-        "homepage": "https://symfony.com",
+        "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+        "homepage": "http://github.com/Seldaek/monolog",
         "keywords": [
-            "compatibility",
-            "polyfill",
-            "portable",
-            "shim"
+            "log",
+            "logging",
+            "psr-3"
         ]
     },
     {
-        "name": "symfony/polyfill-mbstring",
-        "version": "v1.22.1",
-        "version_normalized": "1.22.1.0",
+        "name": "nelexa/zip",
+        "version": "3.3.3",
+        "version_normalized": "3.3.3.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/polyfill-mbstring.git",
-            "reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
+            "url": "https://github.com/Ne-Lexa/php-zip.git",
+            "reference": "501b52f6fc393a599b44ff348a42740e1eaac7c6"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
-            "reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
+            "url": "https://api.github.com/repos/Ne-Lexa/php-zip/zipball/501b52f6fc393a599b44ff348a42740e1eaac7c6",
+            "reference": "501b52f6fc393a599b44ff348a42740e1eaac7c6",
             "shasum": "",
             "mirrors": [
                 {
@@ -633,30 +605,34 @@
             ]
         },
         "require": {
-            "php": ">=7.1"
+            "ext-zlib": "*",
+            "paragonie/random_compat": "*",
+            "php": "^5.5.9 || ^7.0",
+            "psr/http-message": "^1.0",
+            "symfony/finder": "^3.0|^4.0|^5.0"
+        },
+        "require-dev": {
+            "ext-bz2": "*",
+            "ext-fileinfo": "*",
+            "ext-openssl": "*",
+            "ext-xml": "*",
+            "guzzlehttp/psr7": "^1.6",
+            "phpunit/phpunit": "^4.8|^5.7",
+            "symfony/var-dumper": "^3.0|^4.0|^5.0"
         },
         "suggest": {
-            "ext-mbstring": "For best performance"
+            "ext-bz2": "Needed to support BZIP2 compression",
+            "ext-fileinfo": "Needed to get mime-type file",
+            "ext-mcrypt": "Needed to support encrypt zip entries or use ext-openssl",
+            "ext-openssl": "Needed to support encrypt zip entries or use ext-mcrypt"
         },
-        "time": "2021-01-22 09:19:47",
+        "time": "2020-07-11T21:01:42+00:00",
         "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-main": "1.22-dev"
-            },
-            "thanks": {
-                "name": "symfony/polyfill",
-                "url": "https://github.com/symfony/polyfill"
-            }
-        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Polyfill\\Mbstring\\": ""
-            },
-            "files": [
-                "bootstrap.php"
-            ]
+                "PhpZip\\": "src/"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -664,37 +640,36 @@
         ],
         "authors": [
             {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "Ne-Lexa",
+                "email": "alexey@nelexa.ru",
+                "role": "Developer"
             }
         ],
-        "description": "Symfony polyfill for the Mbstring extension",
-        "homepage": "https://symfony.com",
+        "description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, ZipAlign tool, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.",
+        "homepage": "https://github.com/Ne-Lexa/php-zip",
         "keywords": [
-            "compatibility",
-            "mbstring",
-            "polyfill",
-            "portable",
-            "shim"
+            "archive",
+            "extract",
+            "unzip",
+            "winzip",
+            "zip",
+            "zipalign",
+            "ziparchive"
         ]
     },
     {
-        "name": "symfony/polyfill-php72",
-        "version": "v1.22.1",
-        "version_normalized": "1.22.1.0",
+        "name": "overtrue/pinyin",
+        "version": "3.0.6",
+        "version_normalized": "3.0.6.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/polyfill-php72.git",
-            "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9"
+            "url": "https://github.com/overtrue/pinyin.git",
+            "reference": "3b781d267197b74752daa32814d3a2cf5d140779"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
-            "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+            "url": "https://api.github.com/repos/overtrue/pinyin/zipball/3b781d267197b74752daa32814d3a2cf5d140779",
+            "reference": "3b781d267197b74752daa32814d3a2cf5d140779",
             "shasum": "",
             "mirrors": [
                 {
@@ -704,27 +679,18 @@
             ]
         },
         "require": {
-            "php": ">=7.1"
+            "php": ">=5.3"
         },
-        "time": "2021-01-07 16:49:33",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-main": "1.22-dev"
-            },
-            "thanks": {
-                "name": "symfony/polyfill",
-                "url": "https://github.com/symfony/polyfill"
-            }
+        "require-dev": {
+            "phpunit/phpunit": "~4.8"
         },
+        "time": "2017-07-10T07:20:01+00:00",
+        "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Polyfill\\Php72\\": ""
-            },
-            "files": [
-                "bootstrap.php"
-            ]
+                "Overtrue\\Pinyin\\": "src/"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -732,36 +698,31 @@
         ],
         "authors": [
             {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "Carlos",
+                "homepage": "http://github.com/overtrue"
             }
         ],
-        "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
-        "homepage": "https://symfony.com",
+        "description": "Chinese to pinyin translator.",
+        "homepage": "https://github.com/overtrue/pinyin",
         "keywords": [
-            "compatibility",
-            "polyfill",
-            "portable",
-            "shim"
+            "Chinese",
+            "Pinyin",
+            "cn2pinyin"
         ]
     },
     {
-        "name": "symfony/polyfill-intl-normalizer",
-        "version": "v1.22.1",
-        "version_normalized": "1.22.1.0",
+        "name": "overtrue/socialite",
+        "version": "2.0.23",
+        "version_normalized": "2.0.23.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
-            "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
+            "url": "https://github.com/overtrue/socialite.git",
+            "reference": "0bc60597b589592243f074a4d9016aabd2e9cfb2"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
-            "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
+            "url": "https://api.github.com/repos/overtrue/socialite/zipball/0bc60597b589592243f074a4d9016aabd2e9cfb2",
+            "reference": "0bc60597b589592243f074a4d9016aabd2e9cfb2",
             "shasum": "",
             "mirrors": [
                 {
@@ -771,33 +732,22 @@
             ]
         },
         "require": {
-            "php": ">=7.1"
+            "ext-json": "*",
+            "guzzlehttp/guzzle": "^5.0|^6.0|^7.0",
+            "php": ">=5.6",
+            "symfony/http-foundation": "^2.7|^3.0|^4.0|^5.0"
         },
-        "suggest": {
-            "ext-intl": "For best performance"
+        "require-dev": {
+            "mockery/mockery": "~1.2",
+            "phpunit/phpunit": "~6"
         },
-        "time": "2021-01-22 09:19:47",
+        "time": "2020-12-14T03:30:08+00:00",
         "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-main": "1.22-dev"
-            },
-            "thanks": {
-                "name": "symfony/polyfill",
-                "url": "https://github.com/symfony/polyfill"
-            }
-        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
-            },
-            "files": [
-                "bootstrap.php"
-            ],
-            "classmap": [
-                "Resources/stubs"
-            ]
+                "Overtrue\\Socialite\\": "src/"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -805,38 +755,33 @@
         ],
         "authors": [
             {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "overtrue",
+                "email": "anzhengchao@gmail.com"
             }
         ],
-        "description": "Symfony polyfill for intl's Normalizer class and related functions",
-        "homepage": "https://symfony.com",
+        "description": "A collection of OAuth 2 packages that extracts from laravel/socialite.",
         "keywords": [
-            "compatibility",
-            "intl",
-            "normalizer",
-            "polyfill",
-            "portable",
-            "shim"
+            "login",
+            "oauth",
+            "qq",
+            "social",
+            "wechat",
+            "weibo"
         ]
     },
     {
-        "name": "symfony/polyfill-intl-idn",
-        "version": "v1.22.1",
-        "version_normalized": "1.22.1.0",
+        "name": "overtrue/wechat",
+        "version": "4.2.11",
+        "version_normalized": "4.2.11.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/polyfill-intl-idn.git",
-            "reference": "2d63434d922daf7da8dd863e7907e67ee3031483"
+            "url": "https://github.com/w7corp/easywechat.git",
+            "reference": "853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483",
-            "reference": "2d63434d922daf7da8dd863e7907e67ee3031483",
+            "url": "https://api.github.com/repos/w7corp/easywechat/zipball/853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf",
+            "reference": "853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf",
             "shasum": "",
             "mirrors": [
                 {
@@ -846,31 +791,38 @@
             ]
         },
         "require": {
+            "easywechat-composer/easywechat-composer": "^1.1",
+            "ext-fileinfo": "*",
+            "ext-openssl": "*",
+            "ext-simplexml": "*",
+            "guzzlehttp/guzzle": "^6.2",
+            "monolog/monolog": "^1.22 || ^2.0",
+            "overtrue/socialite": "~2.0",
             "php": ">=7.1",
-            "symfony/polyfill-intl-normalizer": "^1.10",
-            "symfony/polyfill-php72": "^1.10"
+            "pimple/pimple": "^3.0",
+            "psr/simple-cache": "^1.0",
+            "symfony/cache": "^3.3 || ^4.3",
+            "symfony/event-dispatcher": "^4.3",
+            "symfony/http-foundation": "^2.7 || ^3.0 || ^4.0",
+            "symfony/psr-http-message-bridge": "^0.3 || ^1.0"
         },
-        "suggest": {
-            "ext-intl": "For best performance"
+        "require-dev": {
+            "friendsofphp/php-cs-fixer": "^2.15",
+            "mikey179/vfsstream": "^1.6",
+            "mockery/mockery": "^1.2.3",
+            "phpstan/phpstan": "^0.11.12",
+            "phpunit/phpunit": "^7.5"
         },
-        "time": "2021-01-22 09:19:47",
+        "time": "2019-11-27T16:38:00+00:00",
         "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-main": "1.22-dev"
-            },
-            "thanks": {
-                "name": "symfony/polyfill",
-                "url": "https://github.com/symfony/polyfill"
-            }
-        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Polyfill\\Intl\\Idn\\": ""
+                "EasyWeChat\\": "src/"
             },
             "files": [
-                "bootstrap.php"
+                "src/Kernel/Support/Helpers.php",
+                "src/Kernel/Helpers.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -879,27 +831,16 @@
         ],
         "authors": [
             {
-                "name": "Laurent Bassin",
-                "email": "laurent@bassin.info"
-            },
-            {
-                "name": "Trevor Rowbotham",
-                "email": "trevor.rowbotham@pm.me"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "overtrue",
+                "email": "anzhengchao@gmail.com"
             }
         ],
-        "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
-        "homepage": "https://symfony.com",
+        "description": "微信SDK",
         "keywords": [
-            "compatibility",
-            "idn",
-            "intl",
-            "polyfill",
-            "portable",
-            "shim"
+            "sdk",
+            "wechat",
+            "weixin",
+            "weixin-sdk"
         ]
     },
     {
@@ -933,7 +874,7 @@
         "suggest": {
             "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
         },
-        "time": "2020-10-15 08:29:30",
+        "time": "2020-10-15T08:29:30+00:00",
         "type": "library",
         "installation-source": "dist",
         "notification-url": "https://packagist.org/downloads/",
@@ -956,18 +897,18 @@
         ]
     },
     {
-        "name": "symfony/mime",
-        "version": "v4.4.21",
-        "version_normalized": "4.4.21.0",
+        "name": "phpoffice/phpspreadsheet",
+        "version": "1.12.0",
+        "version_normalized": "1.12.0.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/mime.git",
-            "reference": "50d7a1d569edad1f1321c59123c4c322c8daff7c"
+            "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+            "reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/mime/zipball/50d7a1d569edad1f1321c59123c4c322c8daff7c",
-            "reference": "50d7a1d569edad1f1321c59123c4c322c8daff7c",
+            "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
+            "reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
             "shasum": "",
             "mirrors": [
                 {
@@ -977,28 +918,47 @@
             ]
         },
         "require": {
-            "php": ">=7.1.3",
-            "symfony/polyfill-intl-idn": "^1.10",
-            "symfony/polyfill-mbstring": "^1.0"
-        },
-        "conflict": {
-            "egulias/email-validator": "~3.0.0",
-            "symfony/mailer": "<4.4"
-        },
-        "require-dev": {
-            "egulias/email-validator": "^2.1.10|^3.1",
-            "symfony/dependency-injection": "^3.4|^4.1|^5.0"
-        },
-        "time": "2021-03-12 08:47:38",
+            "ext-ctype": "*",
+            "ext-dom": "*",
+            "ext-fileinfo": "*",
+            "ext-gd": "*",
+            "ext-iconv": "*",
+            "ext-libxml": "*",
+            "ext-mbstring": "*",
+            "ext-simplexml": "*",
+            "ext-xml": "*",
+            "ext-xmlreader": "*",
+            "ext-xmlwriter": "*",
+            "ext-zip": "*",
+            "ext-zlib": "*",
+            "markbaker/complex": "^1.4",
+            "markbaker/matrix": "^1.2",
+            "php": "^7.1",
+            "psr/simple-cache": "^1.0"
+        },
+        "require-dev": {
+            "dompdf/dompdf": "^0.8.3",
+            "friendsofphp/php-cs-fixer": "^2.16",
+            "jpgraph/jpgraph": "^4.0",
+            "mpdf/mpdf": "^8.0",
+            "phpcompatibility/php-compatibility": "^9.3",
+            "phpunit/phpunit": "^7.5",
+            "squizlabs/php_codesniffer": "^3.5",
+            "tecnickcom/tcpdf": "^6.3"
+        },
+        "suggest": {
+            "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+            "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
+            "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
+            "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
+        },
+        "time": "2020-04-27T08:12:48+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Component\\Mime\\": ""
-            },
-            "exclude-from-classmap": [
-                "/Tests/"
-            ]
+                "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1006,34 +966,50 @@
         ],
         "authors": [
             {
-                "name": "Fabien Potencier",
-                "email": "fabien@symfony.com"
+                "name": "Maarten Balliauw",
+                "homepage": "https://blog.maartenballiauw.be"
             },
             {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "Mark Baker",
+                "homepage": "https://markbakeruk.net"
+            },
+            {
+                "name": "Franck Lefevre",
+                "homepage": "https://rootslabs.net"
+            },
+            {
+                "name": "Erik Tilt"
+            },
+            {
+                "name": "Adrien Crivelli"
             }
         ],
-        "description": "Allows manipulating MIME messages",
-        "homepage": "https://symfony.com",
+        "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+        "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
         "keywords": [
-            "mime",
-            "mime-type"
+            "OpenXML",
+            "excel",
+            "gnumeric",
+            "ods",
+            "php",
+            "spreadsheet",
+            "xls",
+            "xlsx"
         ]
     },
     {
-        "name": "symfony/http-foundation",
-        "version": "v4.4.20",
-        "version_normalized": "4.4.20.0",
+        "name": "pimple/pimple",
+        "version": "v3.2.3",
+        "version_normalized": "3.2.3.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/http-foundation.git",
-            "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a"
+            "url": "https://github.com/silexphp/Pimple.git",
+            "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02d968647fe61b2f419a8dc70c468a9d30a48d3a",
-            "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a",
+            "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
+            "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
             "shasum": "",
             "mirrors": [
                 {
@@ -1043,25 +1019,24 @@
             ]
         },
         "require": {
-            "php": ">=7.1.3",
-            "symfony/mime": "^4.3|^5.0",
-            "symfony/polyfill-mbstring": "~1.1",
-            "symfony/polyfill-php80": "^1.15"
+            "php": ">=5.3.0",
+            "psr/container": "^1.0"
         },
         "require-dev": {
-            "predis/predis": "~1.0",
-            "symfony/expression-language": "^3.4|^4.0|^5.0"
+            "symfony/phpunit-bridge": "^3.2"
         },
-        "time": "2021-02-25 17:11:33",
+        "time": "2018-01-21T07:42:36+00:00",
         "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "3.2.x-dev"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
-            "psr-4": {
-                "Symfony\\Component\\HttpFoundation\\": ""
-            },
-            "exclude-from-classmap": [
-                "/Tests/"
-            ]
+            "psr-0": {
+                "Pimple": "src/"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1071,28 +1046,28 @@
             {
                 "name": "Fabien Potencier",
                 "email": "fabien@symfony.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Defines an object-oriented layer for the HTTP specification",
-        "homepage": "https://symfony.com"
+        "description": "Pimple, a simple Dependency Injection Container",
+        "homepage": "http://pimple.sensiolabs.org",
+        "keywords": [
+            "container",
+            "dependency injection"
+        ]
     },
     {
-        "name": "psr/http-message",
+        "name": "psr/cache",
         "version": "1.0.1",
         "version_normalized": "1.0.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/php-fig/http-message.git",
-            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            "url": "https://github.com/php-fig/cache.git",
+            "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
-            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+            "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+            "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
             "shasum": "",
             "mirrors": [
                 {
@@ -1104,7 +1079,7 @@
         "require": {
             "php": ">=5.3.0"
         },
-        "time": "2016-08-06 14:39:51",
+        "time": "2016-08-06T20:24:11+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
@@ -1114,7 +1089,7 @@
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Psr\\Http\\Message\\": "src/"
+                "Psr\\Cache\\": "src/"
             }
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -1127,30 +1102,26 @@
                 "homepage": "http://www.php-fig.org/"
             }
         ],
-        "description": "Common interface for HTTP messages",
-        "homepage": "https://github.com/php-fig/http-message",
+        "description": "Common interface for caching libraries",
         "keywords": [
-            "http",
-            "http-message",
+            "cache",
             "psr",
-            "psr-7",
-            "request",
-            "response"
+            "psr-6"
         ]
     },
     {
-        "name": "symfony/psr-http-message-bridge",
-        "version": "v1.3.0",
-        "version_normalized": "1.3.0.0",
+        "name": "psr/container",
+        "version": "1.0.0",
+        "version_normalized": "1.0.0.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/psr-http-message-bridge.git",
-            "reference": "9d3e80d54d9ae747ad573cad796e8e247df7b796"
+            "url": "https://github.com/php-fig/container.git",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/9d3e80d54d9ae747ad573cad796e8e247df7b796",
-            "reference": "9d3e80d54d9ae747ad573cad796e8e247df7b796",
+            "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
             "shasum": "",
             "mirrors": [
                 {
@@ -1160,33 +1131,20 @@
             ]
         },
         "require": {
-            "php": "^7.1",
-            "psr/http-message": "^1.0",
-            "symfony/http-foundation": "^4.4 || ^5.0"
-        },
-        "require-dev": {
-            "nyholm/psr7": "^1.1",
-            "symfony/phpunit-bridge": "^4.4 || ^5.0",
-            "zendframework/zend-diactoros": "^1.4.1 || ^2.0"
-        },
-        "suggest": {
-            "nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
+            "php": ">=5.3.0"
         },
-        "time": "2019-11-25 19:33:50",
-        "type": "symfony-bridge",
+        "time": "2017-02-14T16:28:37+00:00",
+        "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.3-dev"
+                "dev-master": "1.0.x-dev"
             }
         },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Bridge\\PsrHttpMessage\\": ""
-            },
-            "exclude-from-classmap": [
-                "/Tests/"
-            ]
+                "Psr\\Container\\": "src/"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1194,36 +1152,33 @@
         ],
         "authors": [
             {
-                "name": "Fabien Potencier",
-                "email": "fabien@symfony.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "http://symfony.com/contributors"
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
             }
         ],
-        "description": "PSR HTTP message bridge",
-        "homepage": "http://symfony.com",
+        "description": "Common Container Interface (PHP FIG PSR-11)",
+        "homepage": "https://github.com/php-fig/container",
         "keywords": [
-            "http",
-            "http-message",
-            "psr-17",
-            "psr-7"
+            "PSR-11",
+            "container",
+            "container-interface",
+            "container-interop",
+            "psr"
         ]
     },
     {
-        "name": "symfony/event-dispatcher-contracts",
-        "version": "v1.1.9",
-        "version_normalized": "1.1.9.0",
+        "name": "psr/http-message",
+        "version": "1.0.1",
+        "version_normalized": "1.0.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/event-dispatcher-contracts.git",
-            "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7"
+            "url": "https://github.com/php-fig/http-message.git",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7",
-            "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+            "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
             "shasum": "",
             "mirrors": [
                 {
@@ -1233,27 +1188,19 @@
             ]
         },
         "require": {
-            "php": ">=7.1.3"
-        },
-        "suggest": {
-            "psr/event-dispatcher": "",
-            "symfony/event-dispatcher-implementation": ""
+            "php": ">=5.3.0"
         },
-        "time": "2020-07-06 13:19:58",
+        "time": "2016-08-06T14:39:51+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.1-dev"
-            },
-            "thanks": {
-                "name": "symfony/contracts",
-                "url": "https://github.com/symfony/contracts"
+                "dev-master": "1.0.x-dev"
             }
         },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Contracts\\EventDispatcher\\": ""
+                "Psr\\Http\\Message\\": "src/"
             }
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -1262,38 +1209,34 @@
         ],
         "authors": [
             {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
             }
         ],
-        "description": "Generic abstractions related to dispatching event",
-        "homepage": "https://symfony.com",
+        "description": "Common interface for HTTP messages",
+        "homepage": "https://github.com/php-fig/http-message",
         "keywords": [
-            "abstractions",
-            "contracts",
-            "decoupling",
-            "interfaces",
-            "interoperability",
-            "standards"
+            "http",
+            "http-message",
+            "psr",
+            "psr-7",
+            "request",
+            "response"
         ]
     },
     {
-        "name": "symfony/event-dispatcher",
-        "version": "v4.4.20",
-        "version_normalized": "4.4.20.0",
+        "name": "psr/log",
+        "version": "1.1.3",
+        "version_normalized": "1.1.3.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/event-dispatcher.git",
-            "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c"
+            "url": "https://github.com/php-fig/log.git",
+            "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c352647244bd376bf7d31efbd5401f13f50dad0c",
-            "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c",
+            "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+            "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
             "shasum": "",
             "mirrors": [
                 {
@@ -1303,40 +1246,75 @@
             ]
         },
         "require": {
-            "php": ">=7.1.3",
-            "symfony/event-dispatcher-contracts": "^1.1"
+            "php": ">=5.3.0"
         },
-        "conflict": {
-            "symfony/dependency-injection": "<3.4"
+        "time": "2020-03-23T09:12:05+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.1.x-dev"
+            }
         },
-        "provide": {
-            "psr/event-dispatcher-implementation": "1.0",
-            "symfony/event-dispatcher-implementation": "1.1"
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\Log\\": "Psr/Log/"
+            }
         },
-        "require-dev": {
-            "psr/log": "~1.0",
-            "symfony/config": "^3.4|^4.0|^5.0",
-            "symfony/dependency-injection": "^3.4|^4.0|^5.0",
-            "symfony/error-handler": "~3.4|~4.4",
-            "symfony/expression-language": "^3.4|^4.0|^5.0",
-            "symfony/http-foundation": "^3.4|^4.0|^5.0",
-            "symfony/service-contracts": "^1.1|^2",
-            "symfony/stopwatch": "^3.4|^4.0|^5.0"
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common interface for logging libraries",
+        "homepage": "https://github.com/php-fig/log",
+        "keywords": [
+            "log",
+            "psr",
+            "psr-3"
+        ]
+    },
+    {
+        "name": "psr/simple-cache",
+        "version": "1.0.1",
+        "version_normalized": "1.0.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/simple-cache.git",
+            "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
         },
-        "suggest": {
-            "symfony/dependency-injection": "",
-            "symfony/http-kernel": ""
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+            "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "php": ">=5.3.0"
         },
-        "time": "2021-01-27 09:09:26",
+        "time": "2017-10-23T01:57:42+00:00",
         "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Component\\EventDispatcher\\": ""
-            },
-            "exclude-from-classmap": [
-                "/Tests/"
-            ]
+                "Psr\\SimpleCache\\": "src/"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1344,30 +1322,32 @@
         ],
         "authors": [
             {
-                "name": "Fabien Potencier",
-                "email": "fabien@symfony.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
             }
         ],
-        "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
-        "homepage": "https://symfony.com"
+        "description": "Common interfaces for simple caching",
+        "keywords": [
+            "cache",
+            "caching",
+            "psr",
+            "psr-16",
+            "simple-cache"
+        ]
     },
     {
-        "name": "symfony/var-exporter",
-        "version": "v4.4.20",
-        "version_normalized": "4.4.20.0",
+        "name": "ralouphie/getallheaders",
+        "version": "3.0.3",
+        "version_normalized": "3.0.3.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/var-exporter.git",
-            "reference": "3a3ea598bba6901d20b58c2579f68700089244ed"
+            "url": "https://github.com/ralouphie/getallheaders.git",
+            "reference": "120b605dfeb996808c31b6477290a714d356e822"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/var-exporter/zipball/3a3ea598bba6901d20b58c2579f68700089244ed",
-            "reference": "3a3ea598bba6901d20b58c2579f68700089244ed",
+            "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+            "reference": "120b605dfeb996808c31b6477290a714d356e822",
             "shasum": "",
             "mirrors": [
                 {
@@ -1377,20 +1357,18 @@
             ]
         },
         "require": {
-            "php": ">=7.1.3"
+            "php": ">=5.6"
         },
         "require-dev": {
-            "symfony/var-dumper": "^4.4.9|^5.0.9"
+            "php-coveralls/php-coveralls": "^2.1",
+            "phpunit/phpunit": "^5 || ^6.5"
         },
-        "time": "2021-01-27 09:09:26",
+        "time": "2019-03-08T08:55:37+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
-            "psr-4": {
-                "Symfony\\Component\\VarExporter\\": ""
-            },
-            "exclude-from-classmap": [
-                "/Tests/"
+            "files": [
+                "src/getallheaders.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -1399,38 +1377,25 @@
         ],
         "authors": [
             {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "Ralph Khattar",
+                "email": "ralph.khattar@gmail.com"
             }
         ],
-        "description": "Allows exporting any serializable PHP data structure to plain PHP code",
-        "homepage": "https://symfony.com",
-        "keywords": [
-            "clone",
-            "construct",
-            "export",
-            "hydrate",
-            "instantiate",
-            "serialize"
-        ]
+        "description": "A polyfill for getallheaders."
     },
     {
-        "name": "psr/container",
-        "version": "1.0.0",
-        "version_normalized": "1.0.0.0",
+        "name": "symfony/cache",
+        "version": "v4.4.21",
+        "version_normalized": "4.4.21.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/php-fig/container.git",
-            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            "url": "https://github.com/symfony/cache.git",
+            "reference": "b7ff54be3f3eb1ce09643692f0c309b1b27bc992"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
-            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "url": "https://api.github.com/repos/symfony/cache/zipball/b7ff54be3f3eb1ce09643692f0c309b1b27bc992",
+            "reference": "b7ff54be3f3eb1ce09643692f0c309b1b27bc992",
             "shasum": "",
             "mirrors": [
                 {
@@ -1440,20 +1405,46 @@
             ]
         },
         "require": {
-            "php": ">=5.3.0"
+            "php": ">=7.1.3",
+            "psr/cache": "^1.0|^2.0",
+            "psr/log": "~1.0",
+            "symfony/cache-contracts": "^1.1.7|^2",
+            "symfony/service-contracts": "^1.1|^2",
+            "symfony/var-exporter": "^4.2|^5.0"
         },
-        "time": "2017-02-14 16:28:37",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.0.x-dev"
-            }
+        "conflict": {
+            "doctrine/dbal": "<2.6",
+            "symfony/dependency-injection": "<3.4",
+            "symfony/http-kernel": "<4.4|>=5.0",
+            "symfony/var-dumper": "<4.4"
+        },
+        "provide": {
+            "psr/cache-implementation": "1.0|2.0",
+            "psr/simple-cache-implementation": "1.0",
+            "symfony/cache-implementation": "1.0|2.0"
+        },
+        "require-dev": {
+            "cache/integration-tests": "dev-master",
+            "doctrine/cache": "^1.6",
+            "doctrine/dbal": "^2.6|^3.0",
+            "predis/predis": "^1.1",
+            "psr/simple-cache": "^1.0",
+            "symfony/config": "^4.2|^5.0",
+            "symfony/dependency-injection": "^3.4|^4.1|^5.0",
+            "symfony/filesystem": "^4.4|^5.0",
+            "symfony/http-kernel": "^4.4",
+            "symfony/var-dumper": "^4.4|^5.0"
         },
+        "time": "2021-03-14T19:28:18+00:00",
+        "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Psr\\Container\\": "src/"
-            }
+                "Symfony\\Component\\Cache\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1461,33 +1452,34 @@
         ],
         "authors": [
             {
-                "name": "PHP-FIG",
-                "homepage": "http://www.php-fig.org/"
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Common Container Interface (PHP FIG PSR-11)",
-        "homepage": "https://github.com/php-fig/container",
+        "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation",
+        "homepage": "https://symfony.com",
         "keywords": [
-            "PSR-11",
-            "container",
-            "container-interface",
-            "container-interop",
-            "psr"
+            "caching",
+            "psr6"
         ]
     },
     {
-        "name": "symfony/service-contracts",
-        "version": "v1.1.9",
-        "version_normalized": "1.1.9.0",
+        "name": "symfony/cache-contracts",
+        "version": "v1.1.10",
+        "version_normalized": "1.1.10.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/service-contracts.git",
-            "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26"
+            "url": "https://github.com/symfony/cache-contracts.git",
+            "reference": "8d5489c10ef90aa7413e4921fc3c0520e24cbed7"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b776d18b303a39f56c63747bcb977ad4b27aca26",
-            "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26",
+            "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/8d5489c10ef90aa7413e4921fc3c0520e24cbed7",
+            "reference": "8d5489c10ef90aa7413e4921fc3c0520e24cbed7",
             "shasum": "",
             "mirrors": [
                 {
@@ -1498,12 +1490,12 @@
         },
         "require": {
             "php": ">=7.1.3",
-            "psr/container": "^1.0"
+            "psr/cache": "^1.0"
         },
         "suggest": {
-            "symfony/service-implementation": ""
+            "symfony/cache-implementation": ""
         },
-        "time": "2020-07-06 13:19:58",
+        "time": "2020-09-02T16:08:58+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
@@ -1517,7 +1509,7 @@
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Contracts\\Service\\": ""
+                "Symfony\\Contracts\\Cache\\": ""
             }
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -1534,7 +1526,7 @@
                 "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Generic abstractions related to writing services",
+        "description": "Generic abstractions related to caching",
         "homepage": "https://symfony.com",
         "keywords": [
             "abstractions",
@@ -1546,18 +1538,18 @@
         ]
     },
     {
-        "name": "psr/cache",
-        "version": "1.0.1",
-        "version_normalized": "1.0.1.0",
+        "name": "symfony/event-dispatcher",
+        "version": "v4.4.20",
+        "version_normalized": "4.4.20.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/php-fig/cache.git",
-            "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            "url": "https://github.com/symfony/event-dispatcher.git",
+            "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
-            "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+            "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c352647244bd376bf7d31efbd5401f13f50dad0c",
+            "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c",
             "shasum": "",
             "mirrors": [
                 {
@@ -1567,20 +1559,40 @@
             ]
         },
         "require": {
-            "php": ">=5.3.0"
+            "php": ">=7.1.3",
+            "symfony/event-dispatcher-contracts": "^1.1"
         },
-        "time": "2016-08-06 20:24:11",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.0.x-dev"
-            }
+        "conflict": {
+            "symfony/dependency-injection": "<3.4"
+        },
+        "provide": {
+            "psr/event-dispatcher-implementation": "1.0",
+            "symfony/event-dispatcher-implementation": "1.1"
+        },
+        "require-dev": {
+            "psr/log": "~1.0",
+            "symfony/config": "^3.4|^4.0|^5.0",
+            "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+            "symfony/error-handler": "~3.4|~4.4",
+            "symfony/expression-language": "^3.4|^4.0|^5.0",
+            "symfony/http-foundation": "^3.4|^4.0|^5.0",
+            "symfony/service-contracts": "^1.1|^2",
+            "symfony/stopwatch": "^3.4|^4.0|^5.0"
+        },
+        "suggest": {
+            "symfony/dependency-injection": "",
+            "symfony/http-kernel": ""
         },
+        "time": "2021-01-27T09:09:26+00:00",
+        "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Psr\\Cache\\": "src/"
-            }
+                "Symfony\\Component\\EventDispatcher\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1588,30 +1600,30 @@
         ],
         "authors": [
             {
-                "name": "PHP-FIG",
-                "homepage": "http://www.php-fig.org/"
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Common interface for caching libraries",
-        "keywords": [
-            "cache",
-            "psr",
-            "psr-6"
-        ]
+        "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+        "homepage": "https://symfony.com"
     },
     {
-        "name": "symfony/cache-contracts",
-        "version": "v1.1.10",
-        "version_normalized": "1.1.10.0",
+        "name": "symfony/event-dispatcher-contracts",
+        "version": "v1.1.9",
+        "version_normalized": "1.1.9.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/cache-contracts.git",
-            "reference": "8d5489c10ef90aa7413e4921fc3c0520e24cbed7"
+            "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+            "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/8d5489c10ef90aa7413e4921fc3c0520e24cbed7",
-            "reference": "8d5489c10ef90aa7413e4921fc3c0520e24cbed7",
+            "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+            "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7",
             "shasum": "",
             "mirrors": [
                 {
@@ -1621,13 +1633,13 @@
             ]
         },
         "require": {
-            "php": ">=7.1.3",
-            "psr/cache": "^1.0"
+            "php": ">=7.1.3"
         },
         "suggest": {
-            "symfony/cache-implementation": ""
+            "psr/event-dispatcher": "",
+            "symfony/event-dispatcher-implementation": ""
         },
-        "time": "2020-09-02 16:08:58",
+        "time": "2020-07-06T13:19:58+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
@@ -1641,7 +1653,7 @@
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Contracts\\Cache\\": ""
+                "Symfony\\Contracts\\EventDispatcher\\": ""
             }
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -1658,7 +1670,7 @@
                 "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Generic abstractions related to caching",
+        "description": "Generic abstractions related to dispatching event",
         "homepage": "https://symfony.com",
         "keywords": [
             "abstractions",
@@ -1670,18 +1682,18 @@
         ]
     },
     {
-        "name": "psr/log",
-        "version": "1.1.3",
-        "version_normalized": "1.1.3.0",
+        "name": "symfony/finder",
+        "version": "v4.4.20",
+        "version_normalized": "4.4.20.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/php-fig/log.git",
-            "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+            "url": "https://github.com/symfony/finder.git",
+            "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
-            "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+            "url": "https://api.github.com/repos/symfony/finder/zipball/2543795ab1570df588b9bbd31e1a2bd7037b94f6",
+            "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6",
             "shasum": "",
             "mirrors": [
                 {
@@ -1691,20 +1703,18 @@
             ]
         },
         "require": {
-            "php": ">=5.3.0"
+            "php": ">=7.1.3"
         },
-        "time": "2020-03-23 09:12:05",
+        "time": "2021-02-12T10:48:09+00:00",
         "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.1.x-dev"
-            }
-        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Psr\\Log\\": "Psr/Log/"
-            }
+                "Symfony\\Component\\Finder\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1712,31 +1722,30 @@
         ],
         "authors": [
             {
-                "name": "PHP-FIG",
-                "homepage": "http://www.php-fig.org/"
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Common interface for logging libraries",
-        "homepage": "https://github.com/php-fig/log",
-        "keywords": [
-            "log",
-            "psr",
-            "psr-3"
-        ]
+        "description": "Finds files and directories via an intuitive fluent interface",
+        "homepage": "https://symfony.com"
     },
     {
-        "name": "symfony/cache",
-        "version": "v4.4.21",
-        "version_normalized": "4.4.21.0",
+        "name": "symfony/http-foundation",
+        "version": "v4.4.20",
+        "version_normalized": "4.4.20.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/cache.git",
-            "reference": "b7ff54be3f3eb1ce09643692f0c309b1b27bc992"
+            "url": "https://github.com/symfony/http-foundation.git",
+            "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/cache/zipball/b7ff54be3f3eb1ce09643692f0c309b1b27bc992",
-            "reference": "b7ff54be3f3eb1ce09643692f0c309b1b27bc992",
+            "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02d968647fe61b2f419a8dc70c468a9d30a48d3a",
+            "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a",
             "shasum": "",
             "mirrors": [
                 {
@@ -1747,41 +1756,20 @@
         },
         "require": {
             "php": ">=7.1.3",
-            "psr/cache": "^1.0|^2.0",
-            "psr/log": "~1.0",
-            "symfony/cache-contracts": "^1.1.7|^2",
-            "symfony/service-contracts": "^1.1|^2",
-            "symfony/var-exporter": "^4.2|^5.0"
-        },
-        "conflict": {
-            "doctrine/dbal": "<2.6",
-            "symfony/dependency-injection": "<3.4",
-            "symfony/http-kernel": "<4.4|>=5.0",
-            "symfony/var-dumper": "<4.4"
-        },
-        "provide": {
-            "psr/cache-implementation": "1.0|2.0",
-            "psr/simple-cache-implementation": "1.0",
-            "symfony/cache-implementation": "1.0|2.0"
+            "symfony/mime": "^4.3|^5.0",
+            "symfony/polyfill-mbstring": "~1.1",
+            "symfony/polyfill-php80": "^1.15"
         },
         "require-dev": {
-            "cache/integration-tests": "dev-master",
-            "doctrine/cache": "^1.6",
-            "doctrine/dbal": "^2.6|^3.0",
-            "predis/predis": "^1.1",
-            "psr/simple-cache": "^1.0",
-            "symfony/config": "^4.2|^5.0",
-            "symfony/dependency-injection": "^3.4|^4.1|^5.0",
-            "symfony/filesystem": "^4.4|^5.0",
-            "symfony/http-kernel": "^4.4",
-            "symfony/var-dumper": "^4.4|^5.0"
+            "predis/predis": "~1.0",
+            "symfony/expression-language": "^3.4|^4.0|^5.0"
         },
-        "time": "2021-03-14 19:28:18",
+        "time": "2021-02-25T17:11:33+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Component\\Cache\\": ""
+                "Symfony\\Component\\HttpFoundation\\": ""
             },
             "exclude-from-classmap": [
                 "/Tests/"
@@ -1793,34 +1781,30 @@
         ],
         "authors": [
             {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
             },
             {
                 "name": "Symfony Community",
                 "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation",
-        "homepage": "https://symfony.com",
-        "keywords": [
-            "caching",
-            "psr6"
-        ]
+        "description": "Defines an object-oriented layer for the HTTP specification",
+        "homepage": "https://symfony.com"
     },
     {
-        "name": "pimple/pimple",
-        "version": "v3.2.3",
-        "version_normalized": "3.2.3.0",
+        "name": "symfony/mime",
+        "version": "v4.4.21",
+        "version_normalized": "4.4.21.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/silexphp/Pimple.git",
-            "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
+            "url": "https://github.com/symfony/mime.git",
+            "reference": "50d7a1d569edad1f1321c59123c4c322c8daff7c"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
-            "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
+            "url": "https://api.github.com/repos/symfony/mime/zipball/50d7a1d569edad1f1321c59123c4c322c8daff7c",
+            "reference": "50d7a1d569edad1f1321c59123c4c322c8daff7c",
             "shasum": "",
             "mirrors": [
                 {
@@ -1830,24 +1814,28 @@
             ]
         },
         "require": {
-            "php": ">=5.3.0",
-            "psr/container": "^1.0"
+            "php": ">=7.1.3",
+            "symfony/polyfill-intl-idn": "^1.10",
+            "symfony/polyfill-mbstring": "^1.0"
+        },
+        "conflict": {
+            "egulias/email-validator": "~3.0.0",
+            "symfony/mailer": "<4.4"
         },
         "require-dev": {
-            "symfony/phpunit-bridge": "^3.2"
+            "egulias/email-validator": "^2.1.10|^3.1",
+            "symfony/dependency-injection": "^3.4|^4.1|^5.0"
         },
-        "time": "2018-01-21 07:42:36",
+        "time": "2021-03-12T08:47:38+00:00",
         "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "3.2.x-dev"
-            }
-        },
         "installation-source": "dist",
         "autoload": {
-            "psr-0": {
-                "Pimple": "src/"
-            }
+            "psr-4": {
+                "Symfony\\Component\\Mime\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -1857,28 +1845,32 @@
             {
                 "name": "Fabien Potencier",
                 "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Pimple, a simple Dependency Injection Container",
-        "homepage": "http://pimple.sensiolabs.org",
+        "description": "Allows manipulating MIME messages",
+        "homepage": "https://symfony.com",
         "keywords": [
-            "container",
-            "dependency injection"
+            "mime",
+            "mime-type"
         ]
     },
     {
-        "name": "ralouphie/getallheaders",
-        "version": "3.0.3",
-        "version_normalized": "3.0.3.0",
+        "name": "symfony/polyfill-intl-idn",
+        "version": "v1.22.1",
+        "version_normalized": "1.22.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/ralouphie/getallheaders.git",
-            "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            "url": "https://github.com/symfony/polyfill-intl-idn.git",
+            "reference": "2d63434d922daf7da8dd863e7907e67ee3031483"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
-            "reference": "120b605dfeb996808c31b6477290a714d356e822",
+            "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483",
+            "reference": "2d63434d922daf7da8dd863e7907e67ee3031483",
             "shasum": "",
             "mirrors": [
                 {
@@ -1888,18 +1880,31 @@
             ]
         },
         "require": {
-            "php": ">=5.6"
+            "php": ">=7.1",
+            "symfony/polyfill-intl-normalizer": "^1.10",
+            "symfony/polyfill-php72": "^1.10"
         },
-        "require-dev": {
-            "php-coveralls/php-coveralls": "^2.1",
-            "phpunit/phpunit": "^5 || ^6.5"
+        "suggest": {
+            "ext-intl": "For best performance"
         },
-        "time": "2019-03-08 08:55:37",
+        "time": "2021-01-22T09:19:47+00:00",
         "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-main": "1.22-dev"
+            },
+            "thanks": {
+                "name": "symfony/polyfill",
+                "url": "https://github.com/symfony/polyfill"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
+            "psr-4": {
+                "Symfony\\Polyfill\\Intl\\Idn\\": ""
+            },
             "files": [
-                "src/getallheaders.php"
+                "bootstrap.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -1908,25 +1913,42 @@
         ],
         "authors": [
             {
-                "name": "Ralph Khattar",
-                "email": "ralph.khattar@gmail.com"
+                "name": "Laurent Bassin",
+                "email": "laurent@bassin.info"
+            },
+            {
+                "name": "Trevor Rowbotham",
+                "email": "trevor.rowbotham@pm.me"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "A polyfill for getallheaders."
+        "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+        "homepage": "https://symfony.com",
+        "keywords": [
+            "compatibility",
+            "idn",
+            "intl",
+            "polyfill",
+            "portable",
+            "shim"
+        ]
     },
     {
-        "name": "guzzlehttp/psr7",
-        "version": "1.8.1",
-        "version_normalized": "1.8.1.0",
+        "name": "symfony/polyfill-intl-normalizer",
+        "version": "v1.22.1",
+        "version_normalized": "1.22.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/guzzle/psr7.git",
-            "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1"
+            "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+            "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1",
-            "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1",
+            "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
+            "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
             "shasum": "",
             "mirrors": [
                 {
@@ -1936,34 +1958,32 @@
             ]
         },
         "require": {
-            "php": ">=5.4.0",
-            "psr/http-message": "~1.0",
-            "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
-        },
-        "provide": {
-            "psr/http-message-implementation": "1.0"
-        },
-        "require-dev": {
-            "ext-zlib": "*",
-            "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+            "php": ">=7.1"
         },
         "suggest": {
-            "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            "ext-intl": "For best performance"
         },
-        "time": "2021-03-21 16:25:00",
+        "time": "2021-01-22T09:19:47+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.7-dev"
+                "dev-main": "1.22-dev"
+            },
+            "thanks": {
+                "name": "symfony/polyfill",
+                "url": "https://github.com/symfony/polyfill"
             }
         },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "GuzzleHttp\\Psr7\\": "src/"
+                "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
             },
             "files": [
-                "src/functions_include.php"
+                "bootstrap.php"
+            ],
+            "classmap": [
+                "Resources/stubs"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -1972,40 +1992,38 @@
         ],
         "authors": [
             {
-                "name": "Michael Dowling",
-                "email": "mtdowling@gmail.com",
-                "homepage": "https://github.com/mtdowling"
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
             },
             {
-                "name": "Tobias Schultze",
-                "homepage": "https://github.com/Tobion"
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "PSR-7 message implementation that also provides common utility methods",
+        "description": "Symfony polyfill for intl's Normalizer class and related functions",
+        "homepage": "https://symfony.com",
         "keywords": [
-            "http",
-            "message",
-            "psr-7",
-            "request",
-            "response",
-            "stream",
-            "uri",
-            "url"
+            "compatibility",
+            "intl",
+            "normalizer",
+            "polyfill",
+            "portable",
+            "shim"
         ]
     },
     {
-        "name": "guzzlehttp/promises",
-        "version": "1.4.1",
-        "version_normalized": "1.4.1.0",
+        "name": "symfony/polyfill-mbstring",
+        "version": "v1.22.1",
+        "version_normalized": "1.22.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/guzzle/promises.git",
-            "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d"
+            "url": "https://github.com/symfony/polyfill-mbstring.git",
+            "reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d",
-            "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+            "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
+            "reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
             "shasum": "",
             "mirrors": [
                 {
@@ -2015,25 +2033,29 @@
             ]
         },
         "require": {
-            "php": ">=5.5"
+            "php": ">=7.1"
         },
-        "require-dev": {
-            "symfony/phpunit-bridge": "^4.4 || ^5.1"
+        "suggest": {
+            "ext-mbstring": "For best performance"
         },
-        "time": "2021-03-07 09:25:29",
+        "time": "2021-01-22T09:19:47+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "1.4-dev"
+                "dev-main": "1.22-dev"
+            },
+            "thanks": {
+                "name": "symfony/polyfill",
+                "url": "https://github.com/symfony/polyfill"
             }
         },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "GuzzleHttp\\Promise\\": "src/"
+                "Symfony\\Polyfill\\Mbstring\\": ""
             },
             "files": [
-                "src/functions_include.php"
+                "bootstrap.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -2042,29 +2064,37 @@
         ],
         "authors": [
             {
-                "name": "Michael Dowling",
-                "email": "mtdowling@gmail.com",
-                "homepage": "https://github.com/mtdowling"
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Guzzle promises library",
+        "description": "Symfony polyfill for the Mbstring extension",
+        "homepage": "https://symfony.com",
         "keywords": [
-            "promise"
+            "compatibility",
+            "mbstring",
+            "polyfill",
+            "portable",
+            "shim"
         ]
     },
     {
-        "name": "guzzlehttp/guzzle",
-        "version": "6.5.5",
-        "version_normalized": "6.5.5.0",
+        "name": "symfony/polyfill-php72",
+        "version": "v1.22.1",
+        "version_normalized": "1.22.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/guzzle/guzzle.git",
-            "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e"
+            "url": "https://github.com/symfony/polyfill-php72.git",
+            "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
-            "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+            "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+            "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
             "shasum": "",
             "mirrors": [
                 {
@@ -2074,34 +2104,26 @@
             ]
         },
         "require": {
-            "ext-json": "*",
-            "guzzlehttp/promises": "^1.0",
-            "guzzlehttp/psr7": "^1.6.1",
-            "php": ">=5.5",
-            "symfony/polyfill-intl-idn": "^1.17.0"
-        },
-        "require-dev": {
-            "ext-curl": "*",
-            "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
-            "psr/log": "^1.1"
-        },
-        "suggest": {
-            "psr/log": "Required for using the Log middleware"
+            "php": ">=7.1"
         },
-        "time": "2020-06-16 21:01:06",
+        "time": "2021-01-07T16:49:33+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
-                "dev-master": "6.5-dev"
+                "dev-main": "1.22-dev"
+            },
+            "thanks": {
+                "name": "symfony/polyfill",
+                "url": "https://github.com/symfony/polyfill"
             }
         },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "GuzzleHttp\\": "src/"
+                "Symfony\\Polyfill\\Php72\\": ""
             },
             "files": [
-                "src/functions_include.php"
+                "bootstrap.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -2110,36 +2132,36 @@
         ],
         "authors": [
             {
-                "name": "Michael Dowling",
-                "email": "mtdowling@gmail.com",
-                "homepage": "https://github.com/mtdowling"
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Guzzle is a PHP HTTP client library",
-        "homepage": "http://guzzlephp.org/",
+        "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+        "homepage": "https://symfony.com",
         "keywords": [
-            "client",
-            "curl",
-            "framework",
-            "http",
-            "http client",
-            "rest",
-            "web service"
+            "compatibility",
+            "polyfill",
+            "portable",
+            "shim"
         ]
     },
     {
-        "name": "overtrue/socialite",
-        "version": "2.0.23",
-        "version_normalized": "2.0.23.0",
+        "name": "symfony/polyfill-php80",
+        "version": "v1.22.1",
+        "version_normalized": "1.22.1.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/overtrue/socialite.git",
-            "reference": "0bc60597b589592243f074a4d9016aabd2e9cfb2"
+            "url": "https://github.com/symfony/polyfill-php80.git",
+            "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/overtrue/socialite/zipball/0bc60597b589592243f074a4d9016aabd2e9cfb2",
-            "reference": "0bc60597b589592243f074a4d9016aabd2e9cfb2",
+            "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
+            "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
             "shasum": "",
             "mirrors": [
                 {
@@ -2149,22 +2171,30 @@
             ]
         },
         "require": {
-            "ext-json": "*",
-            "guzzlehttp/guzzle": "^5.0|^6.0|^7.0",
-            "php": ">=5.6",
-            "symfony/http-foundation": "^2.7|^3.0|^4.0|^5.0"
-        },
-        "require-dev": {
-            "mockery/mockery": "~1.2",
-            "phpunit/phpunit": "~6"
+            "php": ">=7.1"
         },
-        "time": "2020-12-14 03:30:08",
+        "time": "2021-01-07T16:49:33+00:00",
         "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-main": "1.22-dev"
+            },
+            "thanks": {
+                "name": "symfony/polyfill",
+                "url": "https://github.com/symfony/polyfill"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Overtrue\\Socialite\\": "src/"
-            }
+                "Symfony\\Polyfill\\Php80\\": ""
+            },
+            "files": [
+                "bootstrap.php"
+            ],
+            "classmap": [
+                "Resources/stubs"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
@@ -2172,33 +2202,40 @@
         ],
         "authors": [
             {
-                "name": "overtrue",
-                "email": "anzhengchao@gmail.com"
+                "name": "Ion Bazan",
+                "email": "ion.bazan@gmail.com"
+            },
+            {
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "A collection of OAuth 2 packages that extracts from laravel/socialite.",
+        "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+        "homepage": "https://symfony.com",
         "keywords": [
-            "login",
-            "oauth",
-            "qq",
-            "social",
-            "wechat",
-            "weibo"
+            "compatibility",
+            "polyfill",
+            "portable",
+            "shim"
         ]
     },
     {
-        "name": "monolog/monolog",
-        "version": "1.26.0",
-        "version_normalized": "1.26.0.0",
+        "name": "symfony/psr-http-message-bridge",
+        "version": "v1.3.0",
+        "version_normalized": "1.3.0.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/Seldaek/monolog.git",
-            "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33"
+            "url": "https://github.com/symfony/psr-http-message-bridge.git",
+            "reference": "9d3e80d54d9ae747ad573cad796e8e247df7b796"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/Seldaek/monolog/zipball/2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
-            "reference": "2209ddd84e7ef1256b7af205d0717fb62cfc9c33",
+            "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/9d3e80d54d9ae747ad573cad796e8e247df7b796",
+            "reference": "9d3e80d54d9ae747ad573cad796e8e247df7b796",
             "shasum": "",
             "mirrors": [
                 {
@@ -2208,43 +2245,100 @@
             ]
         },
         "require": {
-            "php": ">=5.3.0",
-            "psr/log": "~1.0"
-        },
-        "provide": {
-            "psr/log-implementation": "1.0.0"
+            "php": "^7.1",
+            "psr/http-message": "^1.0",
+            "symfony/http-foundation": "^4.4 || ^5.0"
         },
         "require-dev": {
-            "aws/aws-sdk-php": "^2.4.9 || ^3.0",
-            "doctrine/couchdb": "~1.0@dev",
-            "graylog2/gelf-php": "~1.0",
-            "php-amqplib/php-amqplib": "~2.4",
-            "php-console/php-console": "^3.1.3",
-            "phpstan/phpstan": "^0.12.59",
-            "phpunit/phpunit": "~4.5",
-            "ruflin/elastica": ">=0.90 <3.0",
-            "sentry/sentry": "^0.13",
-            "swiftmailer/swiftmailer": "^5.3|^6.0"
+            "nyholm/psr7": "^1.1",
+            "symfony/phpunit-bridge": "^4.4 || ^5.0",
+            "zendframework/zend-diactoros": "^1.4.1 || ^2.0"
         },
         "suggest": {
-            "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
-            "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
-            "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
-            "ext-mongo": "Allow sending log messages to a MongoDB server",
-            "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
-            "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
-            "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
-            "php-console/php-console": "Allow sending log messages to Google Chrome",
-            "rollbar/rollbar": "Allow sending log messages to Rollbar",
-            "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
-            "sentry/sentry": "Allow sending log messages to a Sentry server"
+            "nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
+        },
+        "time": "2019-11-25T19:33:50+00:00",
+        "type": "symfony-bridge",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.3-dev"
+            }
         },
-        "time": "2020-12-14 12:56:38",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Bridge\\PsrHttpMessage\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "http://symfony.com/contributors"
+            }
+        ],
+        "description": "PSR HTTP message bridge",
+        "homepage": "http://symfony.com",
+        "keywords": [
+            "http",
+            "http-message",
+            "psr-17",
+            "psr-7"
+        ]
+    },
+    {
+        "name": "symfony/service-contracts",
+        "version": "v1.1.9",
+        "version_normalized": "1.1.9.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/service-contracts.git",
+            "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b776d18b303a39f56c63747bcb977ad4b27aca26",
+            "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "php": ">=7.1.3",
+            "psr/container": "^1.0"
+        },
+        "suggest": {
+            "symfony/service-implementation": ""
+        },
+        "time": "2020-07-06T13:19:58+00:00",
         "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.1-dev"
+            },
+            "thanks": {
+                "name": "symfony/contracts",
+                "url": "https://github.com/symfony/contracts"
+            }
+        },
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Monolog\\": "src/Monolog"
+                "Symfony\\Contracts\\Service\\": ""
             }
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -2253,32 +2347,38 @@
         ],
         "authors": [
             {
-                "name": "Jordi Boggiano",
-                "email": "j.boggiano@seld.be",
-                "homepage": "http://seld.be"
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
             }
         ],
-        "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
-        "homepage": "http://github.com/Seldaek/monolog",
+        "description": "Generic abstractions related to writing services",
+        "homepage": "https://symfony.com",
         "keywords": [
-            "log",
-            "logging",
-            "psr-3"
+            "abstractions",
+            "contracts",
+            "decoupling",
+            "interfaces",
+            "interoperability",
+            "standards"
         ]
     },
     {
-        "name": "overtrue/wechat",
-        "version": "4.2.11",
-        "version_normalized": "4.2.11.0",
+        "name": "symfony/var-exporter",
+        "version": "v4.4.20",
+        "version_normalized": "4.4.20.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/w7corp/easywechat.git",
-            "reference": "853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf"
+            "url": "https://github.com/symfony/var-exporter.git",
+            "reference": "3a3ea598bba6901d20b58c2579f68700089244ed"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/w7corp/easywechat/zipball/853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf",
-            "reference": "853e0772e6aa53a71edf1b5d251c7ff1e6b2a2bf",
+            "url": "https://api.github.com/repos/symfony/var-exporter/zipball/3a3ea598bba6901d20b58c2579f68700089244ed",
+            "reference": "3a3ea598bba6901d20b58c2579f68700089244ed",
             "shasum": "",
             "mirrors": [
                 {
@@ -2288,38 +2388,20 @@
             ]
         },
         "require": {
-            "easywechat-composer/easywechat-composer": "^1.1",
-            "ext-fileinfo": "*",
-            "ext-openssl": "*",
-            "ext-simplexml": "*",
-            "guzzlehttp/guzzle": "^6.2",
-            "monolog/monolog": "^1.22 || ^2.0",
-            "overtrue/socialite": "~2.0",
-            "php": ">=7.1",
-            "pimple/pimple": "^3.0",
-            "psr/simple-cache": "^1.0",
-            "symfony/cache": "^3.3 || ^4.3",
-            "symfony/event-dispatcher": "^4.3",
-            "symfony/http-foundation": "^2.7 || ^3.0 || ^4.0",
-            "symfony/psr-http-message-bridge": "^0.3 || ^1.0"
+            "php": ">=7.1.3"
         },
         "require-dev": {
-            "friendsofphp/php-cs-fixer": "^2.15",
-            "mikey179/vfsstream": "^1.6",
-            "mockery/mockery": "^1.2.3",
-            "phpstan/phpstan": "^0.11.12",
-            "phpunit/phpunit": "^7.5"
+            "symfony/var-dumper": "^4.4.9|^5.0.9"
         },
-        "time": "2019-11-27 16:38:00",
+        "time": "2021-01-27T09:09:26+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "EasyWeChat\\": "src/"
+                "Symfony\\Component\\VarExporter\\": ""
             },
-            "files": [
-                "src/Kernel/Support/Helpers.php",
-                "src/Kernel/Helpers.php"
+            "exclude-from-classmap": [
+                "/Tests/"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
@@ -2328,16 +2410,69 @@
         ],
         "authors": [
             {
-                "name": "overtrue",
-                "email": "anzhengchao@gmail.com"
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Allows exporting any serializable PHP data structure to plain PHP code",
+        "homepage": "https://symfony.com",
+        "keywords": [
+            "clone",
+            "construct",
+            "export",
+            "hydrate",
+            "instantiate",
+            "serialize"
+        ]
+    },
+    {
+        "name": "topthink/framework",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://gitee.com/karson/framework",
+            "reference": "7d08e64b4d8e3352c0f855e63513d85aeaa41349"
+        },
+        "require": {
+            "php": ">=5.4.0",
+            "topthink/think-installer": "~1.0"
+        },
+        "require-dev": {
+            "johnkary/phpunit-speedtrap": "^1.0",
+            "mikey179/vfsstream": "~1.6",
+            "phpdocumentor/reflection-docblock": "^2.0",
+            "phploc/phploc": "2.*",
+            "phpunit/phpunit": "4.8.*",
+            "sebastian/phpcpd": "2.*"
+        },
+        "time": "2021-03-17T09:43:15+00:00",
+        "type": "think-framework",
+        "installation-source": "source",
+        "autoload": {
+            "psr-4": {
+                "think\\": "library/think"
+            }
+        },
+        "license": [
+            "Apache-2.0"
+        ],
+        "authors": [
+            {
+                "name": "liu21st",
+                "email": "liu21st@gmail.com"
             }
         ],
-        "description": "微信SDK",
+        "description": "the new thinkphp framework",
+        "homepage": "http://thinkphp.cn/",
         "keywords": [
-            "sdk",
-            "wechat",
-            "weixin",
-            "weixin-sdk"
+            "ORM",
+            "framework",
+            "thinkphp"
         ]
     },
     {
@@ -2361,7 +2496,7 @@
                 }
             ]
         },
-        "time": "2016-07-06 01:47:11",
+        "time": "2016-07-06T01:47:11+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
@@ -2385,18 +2520,18 @@
         "description": "captcha package for thinkphp5"
     },
     {
-        "name": "symfony/finder",
-        "version": "v4.4.20",
-        "version_normalized": "4.4.20.0",
+        "name": "topthink/think-helper",
+        "version": "v1.0.7",
+        "version_normalized": "1.0.7.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/symfony/finder.git",
-            "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6"
+            "url": "https://github.com/top-think/think-helper.git",
+            "reference": "5f92178606c8ce131d36b37a57c58eb71e55f019"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/finder/zipball/2543795ab1570df588b9bbd31e1a2bd7037b94f6",
-            "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6",
+            "url": "https://api.github.com/repos/top-think/think-helper/zipball/5f92178606c8ce131d36b37a57c58eb71e55f019",
+            "reference": "5f92178606c8ce131d36b37a57c58eb71e55f019",
             "shasum": "",
             "mirrors": [
                 {
@@ -2405,50 +2540,42 @@
                 }
             ]
         },
-        "require": {
-            "php": ">=7.1.3"
-        },
-        "time": "2021-02-12 10:48:09",
+        "time": "2018-10-05T00:43:21+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Symfony\\Component\\Finder\\": ""
+                "think\\helper\\": "src"
             },
-            "exclude-from-classmap": [
-                "/Tests/"
+            "files": [
+                "src/helper.php"
             ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "MIT"
+            "Apache-2.0"
         ],
         "authors": [
             {
-                "name": "Fabien Potencier",
-                "email": "fabien@symfony.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
+                "name": "yunwuxin",
+                "email": "448901948@qq.com"
             }
         ],
-        "description": "Finds files and directories via an intuitive fluent interface",
-        "homepage": "https://symfony.com"
+        "description": "The ThinkPHP5 Helper Package"
     },
     {
-        "name": "nelexa/zip",
-        "version": "3.3.3",
-        "version_normalized": "3.3.3.0",
+        "name": "topthink/think-installer",
+        "version": "v1.0.14",
+        "version_normalized": "1.0.14.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/Ne-Lexa/php-zip.git",
-            "reference": "501b52f6fc393a599b44ff348a42740e1eaac7c6"
+            "url": "https://github.com/top-think/think-installer.git",
+            "reference": "eae1740ac264a55c06134b6685dfb9f837d004d1"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/Ne-Lexa/php-zip/zipball/501b52f6fc393a599b44ff348a42740e1eaac7c6",
-            "reference": "501b52f6fc393a599b44ff348a42740e1eaac7c6",
+            "url": "https://api.github.com/repos/top-think/think-installer/zipball/eae1740ac264a55c06134b6685dfb9f837d004d1",
+            "reference": "eae1740ac264a55c06134b6685dfb9f837d004d1",
             "shasum": "",
             "mirrors": [
                 {
@@ -2458,71 +2585,46 @@
             ]
         },
         "require": {
-            "ext-zlib": "*",
-            "paragonie/random_compat": "*",
-            "php": "^5.5.9 || ^7.0",
-            "psr/http-message": "^1.0",
-            "symfony/finder": "^3.0|^4.0|^5.0"
+            "composer-plugin-api": "^1.0||^2.0"
         },
         "require-dev": {
-            "ext-bz2": "*",
-            "ext-fileinfo": "*",
-            "ext-openssl": "*",
-            "ext-xml": "*",
-            "guzzlehttp/psr7": "^1.6",
-            "phpunit/phpunit": "^4.8|^5.7",
-            "symfony/var-dumper": "^3.0|^4.0|^5.0"
+            "composer/composer": "^1.0||^2.0"
         },
-        "suggest": {
-            "ext-bz2": "Needed to support BZIP2 compression",
-            "ext-fileinfo": "Needed to get mime-type file",
-            "ext-mcrypt": "Needed to support encrypt zip entries or use ext-openssl",
-            "ext-openssl": "Needed to support encrypt zip entries or use ext-mcrypt"
+        "time": "2021-03-25T08:34:02+00:00",
+        "type": "composer-plugin",
+        "extra": {
+            "class": "think\\composer\\Plugin"
         },
-        "time": "2020-07-11 21:01:42",
-        "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "PhpZip\\": "src/"
+                "think\\composer\\": "src"
             }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "MIT"
+            "Apache-2.0"
         ],
         "authors": [
             {
-                "name": "Ne-Lexa",
-                "email": "alexey@nelexa.ru",
-                "role": "Developer"
+                "name": "yunwuxin",
+                "email": "448901948@qq.com"
             }
-        ],
-        "description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, ZipAlign tool, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.",
-        "homepage": "https://github.com/Ne-Lexa/php-zip",
-        "keywords": [
-            "archive",
-            "extract",
-            "unzip",
-            "winzip",
-            "zip",
-            "zipalign",
-            "ziparchive"
         ]
     },
     {
-        "name": "overtrue/pinyin",
-        "version": "3.0.6",
-        "version_normalized": "3.0.6.0",
+        "name": "topthink/think-queue",
+        "version": "v1.1.6",
+        "version_normalized": "1.1.6.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/overtrue/pinyin.git",
-            "reference": "3b781d267197b74752daa32814d3a2cf5d140779"
+            "url": "https://github.com/top-think/think-queue.git",
+            "reference": "250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/overtrue/pinyin/zipball/3b781d267197b74752daa32814d3a2cf5d140779",
-            "reference": "3b781d267197b74752daa32814d3a2cf5d140779",
+            "url": "https://api.github.com/repos/top-think/think-queue/zipball/250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245",
+            "reference": "250650eb0e8ea5af4cfdc7ae46f3f4e0a24ac245",
             "shasum": "",
             "mirrors": [
                 {
@@ -2532,36 +2634,39 @@
             ]
         },
         "require": {
-            "php": ">=5.3"
+            "topthink/think-helper": ">=1.0.4",
+            "topthink/think-installer": ">=1.0.10"
         },
         "require-dev": {
-            "phpunit/phpunit": "~4.8"
+            "topthink/framework": "~5.0.0"
+        },
+        "time": "2018-10-15T10:16:55+00:00",
+        "type": "think-extend",
+        "extra": {
+            "think-config": {
+                "queue": "src/config.php"
+            }
         },
-        "time": "2017-07-10 07:20:01",
-        "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "Overtrue\\Pinyin\\": "src/"
-            }
+                "think\\": "src"
+            },
+            "files": [
+                "src/common.php"
+            ]
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "MIT"
+            "Apache-2.0"
         ],
         "authors": [
             {
-                "name": "Carlos",
-                "homepage": "http://github.com/overtrue"
+                "name": "yunwuxin",
+                "email": "448901948@qq.com"
             }
         ],
-        "description": "Chinese to pinyin translator.",
-        "homepage": "https://github.com/overtrue/pinyin",
-        "keywords": [
-            "Chinese",
-            "Pinyin",
-            "cn2pinyin"
-        ]
+        "description": "The ThinkPHP5 Queue Package"
     },
     {
         "name": "txthinking/mailer",
@@ -2592,7 +2697,7 @@
             "monolog/monolog": "~1.13",
             "phpunit/phpunit": "~4.0"
         },
-        "time": "2018-10-09 10:47:23",
+        "time": "2018-10-09T10:47:23+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
@@ -2624,18 +2729,18 @@
         ]
     },
     {
-        "name": "karsonzhang/fastadmin-addons",
-        "version": "1.2.10",
-        "version_normalized": "1.2.10.0",
+        "name": "workerman/channel",
+        "version": "v1.1.0",
+        "version_normalized": "1.1.0.0",
         "source": {
             "type": "git",
-            "url": "https://github.com/karsonzhang/fastadmin-addons.git",
-            "reference": "276dbed2bcaaa4eaab2019b36e4886123c2d985c"
+            "url": "https://github.com/walkor/channel.git",
+            "reference": "3df772d0d20d4cebfcfd621c33d1a1ab732db523"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/karsonzhang/fastadmin-addons/zipball/276dbed2bcaaa4eaab2019b36e4886123c2d985c",
-            "reference": "276dbed2bcaaa4eaab2019b36e4886123c2d985c",
+            "url": "https://api.github.com/repos/walkor/channel/zipball/3df772d0d20d4cebfcfd621c33d1a1ab732db523",
+            "reference": "3df772d0d20d4cebfcfd621c33d1a1ab732db523",
             "shasum": "",
             "mirrors": [
                 {
@@ -2645,87 +2750,136 @@
             ]
         },
         "require": {
-            "nelexa/zip": "^3.3",
-            "php": ">=7.0.0",
-            "symfony/var-exporter": "^4.4.13"
+            "workerman/workerman": ">=4.0.12"
         },
-        "time": "2021-04-07 04:02:27",
+        "time": "2021-02-08T02:45:42+00:00",
         "type": "library",
-        "extra": {
-            "think-config": {
-                "addons": "src/config.php"
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Channel\\": "./src"
             }
         },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "homepage": "http://www.workerman.net"
+    },
+    {
+        "name": "workerman/phpsocket.io",
+        "version": "v1.1.13",
+        "version_normalized": "1.1.13.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/walkor/phpsocket.io.git",
+            "reference": "25243e320bf6b43f11b96974d1a54241dcd07bce"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/walkor/phpsocket.io/zipball/25243e320bf6b43f11b96974d1a54241dcd07bce",
+            "reference": "25243e320bf6b43f11b96974d1a54241dcd07bce",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "workerman/channel": ">=1.0.0",
+            "workerman/workerman": ">=3.5.16"
+        },
+        "time": "2022-01-19T07:54:05+00:00",
+        "type": "library",
         "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "think\\": "src/"
-            },
-            "files": [
-                "src/common.php"
-            ]
+                "PHPSocketIO\\": "./src"
+            }
         },
         "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "Apache-2.0"
+            "MIT"
         ],
-        "authors": [
+        "homepage": "http://www.workerman.net",
+        "keywords": [
+            "Socket.io"
+        ],
+        "funding": [
             {
-                "name": "Karson",
-                "email": "karson@fastadmin.net"
+                "url": "https://opencollective.com/walkor",
+                "type": "open_collective"
             },
             {
-                "name": "xiaobo.sun",
-                "email": "xiaobo.sun@qq.com"
+                "url": "https://www.patreon.com/walkor",
+                "type": "patreon"
             }
-        ],
-        "description": "addons package for fastadmin",
-        "homepage": "https://github.com/karsonzhang/fastadmin-addons"
+        ]
     },
     {
-        "name": "topthink/framework",
-        "version": "dev-master",
-        "version_normalized": "9999999-dev",
+        "name": "workerman/workerman",
+        "version": "v4.0.28",
+        "version_normalized": "4.0.28.0",
         "source": {
             "type": "git",
-            "url": "https://gitee.com/karson/framework",
-            "reference": "7d08e64b4d8e3352c0f855e63513d85aeaa41349"
+            "url": "https://github.com/walkor/workerman.git",
+            "reference": "413e2a3eb96fb591b8456654b3fd2939f3a84cf1"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/walkor/workerman/zipball/413e2a3eb96fb591b8456654b3fd2939f3a84cf1",
+            "reference": "413e2a3eb96fb591b8456654b3fd2939f3a84cf1",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
         },
         "require": {
-            "php": ">=5.4.0",
-            "topthink/think-installer": "~1.0"
+            "php": ">=5.3"
         },
-        "require-dev": {
-            "johnkary/phpunit-speedtrap": "^1.0",
-            "mikey179/vfsstream": "~1.6",
-            "phpdocumentor/reflection-docblock": "^2.0",
-            "phploc/phploc": "2.*",
-            "phpunit/phpunit": "4.8.*",
-            "sebastian/phpcpd": "2.*"
+        "suggest": {
+            "ext-event": "For better performance. "
         },
-        "time": "2021-03-17 09:43:15",
-        "type": "think-framework",
-        "installation-source": "source",
+        "time": "2022-02-18T15:34:08+00:00",
+        "type": "library",
+        "installation-source": "dist",
         "autoload": {
             "psr-4": {
-                "think\\": "library/think"
+                "Workerman\\": "./"
             }
         },
+        "notification-url": "https://packagist.org/downloads/",
         "license": [
-            "Apache-2.0"
+            "MIT"
         ],
         "authors": [
             {
-                "name": "liu21st",
-                "email": "liu21st@gmail.com"
+                "name": "walkor",
+                "email": "walkor@workerman.net",
+                "homepage": "http://www.workerman.net",
+                "role": "Developer"
             }
         ],
-        "description": "the new thinkphp framework",
-        "homepage": "http://thinkphp.cn/",
+        "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
+        "homepage": "http://www.workerman.net",
         "keywords": [
-            "ORM",
-            "framework",
-            "thinkphp"
+            "asynchronous",
+            "event-loop"
+        ],
+        "funding": [
+            {
+                "url": "https://opencollective.com/workerman",
+                "type": "open_collective"
+            },
+            {
+                "url": "https://www.patreon.com/walkor",
+                "type": "patreon"
+            }
         ]
     }
 ]
diff --git a/vendor/workerman/channel/README.md b/vendor/workerman/channel/README.md
new file mode 100644
index 0000000..0a4f7d8
--- /dev/null
+++ b/vendor/workerman/channel/README.md
@@ -0,0 +1,101 @@
+# Channel
+基于订阅的多进程通讯组件,用于workerman进程间通讯或者服务器集群通讯,类似redis订阅发布机制。基于workerman开发。
+
+Channel 提供两种通讯形式,分别是发布订阅的事件机制和消息队列机制。
+
+它们的主要区别是:
+- 事件机制是消息发出后,所有订阅该事件的客户端都能收到消息。
+- 消息队列机制是消息发出后,所有订阅该消息的客户端只有一个会收到消息,如果客户端忙消息会进行排队直到有客户端闲置后重新取到消息。
+- 需要注意的是 Channel 只是提供一种通讯方式,本身并不提供消息确认、重试、延迟、持久化等功能,请根据实际情况合理使用。
+
+# 手册地址
+[Channel手册](http://doc.workerman.net/components/channel.html)
+
+# 服务端
+```php
+use Workerman\Worker;
+
+//Tcp 通讯方式
+$channel_server = new Channel\Server('0.0.0.0', 2206);
+
+//Unix Domain Socket 通讯方式
+//$channel_server = new Channel\Server('unix:///tmp/workerman-channel.sock');
+
+if(!defined('GLOBAL_START'))
+{
+    Worker::runAll();
+}
+```
+
+# 客户端
+```php
+use Workerman\Worker;
+
+$worker = new Worker();
+$worker->onWorkerStart = function()
+{
+    // Channel客户端连接到Channel服务端
+    Channel\Client::connect('<Channel服务端ip>', 2206);
+
+    // 使用 Unix Domain Socket 通讯
+    //Channel\Client::connect('unix:///tmp/workerman-channel.sock');
+
+    // 要订阅的事件名称(名称可以为任意的数字和字符串组合)
+    $event_name = 'event_xxxx';
+    // 订阅某个自定义事件并注册回调,收到事件后会自动触发此回调
+    Channel\Client::on($event_name, function($event_data){
+        var_dump($event_data);
+    });
+};
+$worker->onMessage = function($connection, $data)
+{
+    // 要发布的事件名称
+    $event_name = 'event_xxxx';
+    // 事件数据(数据格式可以为数字、字符串、数组),会传递给客户端回调函数作为参数
+    $event_data = array('some data.', 'some data..');
+    // 发布某个自定义事件,订阅这个事件的客户端会收到事件数据,并触发客户端对应的事件回调
+    Channel\Client::publish($event_name, $event_data);
+};
+
+if(!defined('GLOBAL_START'))
+{
+    Worker::runAll();
+}
+````
+
+## 消息队列示例
+```php
+use Workerman\Worker;
+use Workerman\Timer;
+
+$worker = new Worker();
+$worker->name = 'Producer';
+$worker->onWorkerStart = function()
+{
+    Client::connect();
+
+    $count = 0;
+    Timer::add(1, function() {
+        Client::enqueue('queue', 'Hello World '.time());
+    });
+};
+
+$mq = new Worker();
+$mq->name = 'Consumer';
+$mq->count = 4;
+$mq->onWorkerStart = function($worker) {
+    Client::connect();
+
+    //订阅消息 queue
+    Client::watch('queue', function($data) use ($worker) {
+        echo "Worker {$worker->id} get queue: $data\n";
+    });
+
+    //10 秒后取消订阅该消息
+    Timer::add(10, function() {
+        Client::unwatch('queue');
+    }, [], false);
+};
+
+Worker::runAll();
+```
\ No newline at end of file
diff --git a/vendor/workerman/channel/composer.json b/vendor/workerman/channel/composer.json
new file mode 100644
index 0000000..df63a66
--- /dev/null
+++ b/vendor/workerman/channel/composer.json
@@ -0,0 +1,12 @@
+{
+    "name"  : "workerman/channel",
+    "type"  : "library",
+    "homepage": "http://www.workerman.net",
+    "license" : "MIT",
+    "require": {
+        "workerman/workerman" : ">=4.0.12"
+    },
+    "autoload": {
+        "psr-4": {"Channel\\": "./src"}
+    }
+}
diff --git a/vendor/workerman/channel/src/Client.php b/vendor/workerman/channel/src/Client.php
new file mode 100644
index 0000000..fd039af
--- /dev/null
+++ b/vendor/workerman/channel/src/Client.php
@@ -0,0 +1,391 @@
+<?php
+namespace Channel;
+
+use Workerman\Connection\AsyncTcpConnection;
+use Workerman\Lib\Timer;
+use Workerman\Protocols\Frame;
+
+/**
+ * Channel/Client
+ * @version 1.0.7
+ */
+class Client
+{
+    /**
+     * onMessage.
+     * @var callback
+     */
+    public static $onMessage = null;
+
+    /**
+     * onConnect
+     * @var callback
+     */
+    public static $onConnect = null;
+
+    /**
+     * onClose
+     * @var callback
+     */
+    public static $onClose = null;
+
+    /**
+     * Connction to channel server.
+     * @var \Workerman\Connection\TcpConnection
+     */
+    protected static $_remoteConnection = null;
+
+    /**
+     * Channel server ip.
+     * @var string
+     */
+    protected static $_remoteIp = null;
+
+    /**
+     * Channel server port.
+     * @var int
+     */
+    protected static $_remotePort = null;
+
+    /**
+     * Reconnect timer.
+     * @var Timer
+     */
+    protected static $_reconnectTimer = null;
+
+    /**
+     * Ping timer.
+     * @var Timer
+     */
+    protected static $_pingTimer = null;
+
+    /**
+     * All event callback.
+     * @var array
+     */
+    protected static $_events = array();
+
+    /**
+     * All queue callback.
+     * @var callable
+     */
+    protected static $_queues = array();
+
+    /**
+     * @var bool
+     */
+    protected static $_isWorkermanEnv = true;
+
+    /**
+     * Ping interval.
+     * @var int
+     */
+    public static $pingInterval = 25;
+
+    /**
+     * Connect to channel server
+     * @param string $ip Channel server ip address or unix domain socket address
+     * Ip like (TCP): 192.168.1.100
+     * Unix domain socket like: unix:///tmp/workerman-channel.sock
+     * @param int $port Port to connect when use tcp
+     */
+    public static function connect($ip = '127.0.0.1', $port = 2206)
+    {
+        if (self::$_remoteConnection) {
+            return;
+        }
+
+        self::$_remoteIp = $ip;
+        self::$_remotePort = $port;
+
+        if (PHP_SAPI !== 'cli' || !class_exists('Workerman\Worker', false)) {
+            self::$_isWorkermanEnv = false;
+        }
+
+        // For workerman environment.
+        if (self::$_isWorkermanEnv) {
+            if (strpos($ip, 'unix://') === false) {
+                $conn = new AsyncTcpConnection('frame://' . self::$_remoteIp . ':' . self::$_remotePort);
+            } else {
+                $conn = new AsyncTcpConnection($ip);
+                $conn->protocol = Frame::class;
+            }
+
+            $conn->onClose = [self::class, 'onRemoteClose'];
+            $conn->onConnect = [self::class, 'onRemoteConnect'];
+            $conn->onMessage = [self::class , 'onRemoteMessage'];
+            $conn->connect();
+
+            if (empty(self::$_pingTimer)) {
+                self::$_pingTimer = Timer::add(self::$pingInterval, 'Channel\Client::ping');
+            }
+            // Not workerman environment.
+        } else {
+            $remote = strpos($ip, 'unix://') === false ? 'tcp://'.self::$_remoteIp.':'.self::$_remotePort : $ip;
+            $conn = stream_socket_client($remote, $code, $message, 5);
+            if (!$conn) {
+                throw new \Exception($message);
+            }
+        }
+
+        self::$_remoteConnection = $conn;
+    }
+
+    /**
+     * onRemoteMessage.
+     * @param \Workerman\Connection\TcpConnection $connection
+     * @param string $data
+     * @throws \Exception
+     */
+    public static function onRemoteMessage($connection, $data)
+    {
+        $data = unserialize($data);
+        $type = $data['type'];
+        $event = $data['channel'];
+        $event_data = $data['data'];
+
+        $callback = null;
+
+        if ($type == 'event') {
+	        if (!empty(self::$_events[$event])) {
+		        call_user_func(self::$_events[$event], $event_data);
+	        } elseif (!empty(Client::$onMessage)) {
+		        call_user_func(Client::$onMessage, $event, $event_data);
+	        } else {
+		        throw new \Exception("event:$event have not callback");
+	        }
+        } else {
+	        if (isset(self::$_queues[$event])) {
+		        call_user_func(self::$_queues[$event], $event_data);
+	        } else {
+		        throw new \Exception("queue:$event have not callback");
+	        }
+        }
+    }
+
+    /**
+     * Ping.
+     * @return void
+     */
+    public static function ping()
+    {
+        if(self::$_remoteConnection)
+        {
+            self::$_remoteConnection->send('');
+        }
+    }
+
+    /**
+     * onRemoteClose.
+     * @return void
+     */
+    public static function onRemoteClose()
+    {
+        echo "Waring channel connection closed and try to reconnect\n";
+        self::$_remoteConnection = null;
+        self::clearTimer();
+        self::$_reconnectTimer = Timer::add(1, 'Channel\Client::connect', array(self::$_remoteIp, self::$_remotePort));
+        if (self::$onClose) {
+            call_user_func(Client::$onClose);
+        }
+    }
+
+    /**
+     * onRemoteConnect.
+     * @return void
+     */
+    public static function onRemoteConnect()
+    {
+        $all_event_names = array_keys(self::$_events);
+        if($all_event_names)
+        {
+            self::subscribe($all_event_names);
+        }
+        self::clearTimer();
+
+        if (self::$onConnect) {
+            call_user_func(Client::$onConnect);
+        }
+    }
+
+    /**
+     * clearTimer.
+     * @return void
+     */
+    public static function clearTimer()
+    {
+        if (!self::$_isWorkermanEnv) {
+            throw new \Exception('Channel\\Client not support clearTimer method when it is not in the workerman environment.');
+        }
+        if(self::$_reconnectTimer)
+        {
+            Timer::del(self::$_reconnectTimer);
+            self::$_reconnectTimer = null;
+        }
+    }
+
+    /**
+     * On.
+     * @param string $event
+     * @param callback $callback
+     * @throws \Exception
+     */
+    public static function on($event, $callback)
+    {
+        if (!is_callable($callback)) {
+            throw new \Exception('callback is not callable for event.');
+        }
+        self::$_events[$event] = $callback;
+        self::subscribe($event);
+    }
+
+    /**
+     * Subscribe.
+     * @param string $events
+     * @return void
+     */
+    public static function subscribe($events)
+    {
+        $events = (array)$events;
+        self::send(array('type' => 'subscribe', 'channels'=>$events));
+        foreach ($events as $event) {
+            if(!isset(self::$_events[$event])) {
+                self::$_events[$event] = null;
+            }
+        }
+    }
+
+    /**
+     * Unsubscribe.
+     * @param string $events
+     * @return void
+     */
+    public static function unsubscribe($events)
+    {
+        $events = (array)$events;
+        self::send(array('type' => 'unsubscribe', 'channels'=>$events));
+        foreach($events as $event) {
+            unset(self::$_events[$event]);
+        }
+    }
+
+    /**
+     * Publish.
+     * @param string $events
+     * @param mixed $data
+     */
+    public static function publish($events, $data)
+    {
+        self::sendAnyway(array('type' => 'publish', 'channels' => (array)$events, 'data' => $data));
+    }
+
+    /**
+     * Watch a channel of queue
+     * @param string|array $channels
+     * @param callable $callback
+     * @param boolean $autoReserve Auto reserve after callback finished.
+     * But sometime you may don't want reserve immediately, or in some asynchronous job,
+     * you want reserve in finished callback, so you should set $autoReserve to false
+     * and call Client::reserve() after watch() and in finish callback manually.
+     * @throws \Exception
+     */
+    public static function watch($channels, $callback, $autoReserve=true)
+    {
+        if (!is_callable($callback)) {
+            throw new \Exception('callback is not callable for watch.');
+        }
+
+        if ($autoReserve) {
+        	$callback = static function($data) use ($callback) {
+		        try {
+			        call_user_func($callback, $data);
+		        } catch (\Exception $e) {
+			        throw $e;
+		        } catch (\Error $e) {
+			        throw $e;
+		        } finally {
+			        self::reserve();
+		        }
+	        };
+        }
+
+	    $channels = (array)$channels;
+        self::send(array('type' => 'watch', 'channels'=>$channels));
+
+        foreach ($channels as $channel) {
+        	self::$_queues[$channel] = $callback;
+        }
+
+        if ($autoReserve) {
+	        self::reserve();
+        }
+    }
+
+    /**
+     * Unwatch a channel of queue
+     * @param string $channel
+     * @throws \Exception
+     */
+    public static function unwatch($channels)
+    {
+	    $channels = (array)$channels;
+        self::send(array('type' => 'unwatch', 'channels'=>$channels));
+        foreach ($channels as $channel) {
+	        if (isset(self::$_queues[$channel])) {
+		        unset(self::$_queues[$channel]);
+	        }
+        }
+    }
+
+	/**
+	 * Put data to queue
+	 * @param string|array $channels
+	 * @param mixed $data
+	 * @throws \Exception
+	 */
+    public static function enqueue($channels, $data)
+    {
+        self::sendAnyway(array('type' => 'enqueue', 'channels' => (array)$channels, 'data' => $data));
+    }
+
+	/**
+	 * Start reserve queue manual
+	 * @throws \Exception
+	 */
+    public static function reserve()
+    {
+	    self::send(array('type' => 'reserve'));
+    }
+
+    /**
+     * Send through workerman environment
+     * @param $data
+     * @throws \Exception
+     */
+    protected static function send($data)
+    {
+        if (!self::$_isWorkermanEnv) {
+            throw new \Exception("Channel\\Client not support {$data['type']} method when it is not in the workerman environment.");
+        }
+        self::connect(self::$_remoteIp, self::$_remotePort);
+        self::$_remoteConnection->send(serialize($data));
+    }
+
+    /**
+     * Send from any environment
+     * @param $data
+     * @throws \Exception
+     */
+    protected static function sendAnyway($data)
+    {
+        self::connect(self::$_remoteIp, self::$_remotePort);
+        $body = serialize($data);
+        if (self::$_isWorkermanEnv) {
+            self::$_remoteConnection->send($body);
+        } else {
+            $buffer = pack('N', 4+strlen($body)) . $body;
+            fwrite(self::$_remoteConnection, $buffer);
+        }
+    }
+
+}
diff --git a/vendor/workerman/channel/src/Queue.php b/vendor/workerman/channel/src/Queue.php
new file mode 100644
index 0000000..3b1b56e
--- /dev/null
+++ b/vendor/workerman/channel/src/Queue.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Channel;
+
+use Workerman\Connection\TcpConnection;
+
+class Queue
+{
+
+    public $name = 'default';
+    public $watcher = array();
+    public $consumer = array();
+    protected $queue = null;
+
+    public function __construct($name)
+    {
+        $this->name = $name;
+        $this->queue = new \SplQueue();
+    }
+
+    /**
+     * @param TcpConnection $connection
+     */
+    public function addWatch($connection)
+    {
+    	if (!isset($this->watcher[$connection->id])) {
+		    $this->watcher[$connection->id] = $connection;
+		    $connection->watchs[] = $this->name;
+	    }
+    }
+
+    /**
+     * @param TcpConnection $connection
+     */
+    public function removeWatch($connection)
+    {
+        if (isset($connection->watchs) && in_array($this->name, $connection->watchs)) {
+        	$idx = array_search($this->name, $connection->watchs);
+            unset($connection->watchs[$idx]);
+        }
+        if (isset($this->watcher[$connection->id])) {
+            unset($this->watcher[$connection->id]);
+        }
+        if (isset($this->consumer[$connection->id])) {
+            unset($this->consumer[$connection->id]);
+        }
+    }
+
+	/**
+	 * @param TcpConnection $connection
+	 */
+    public function addConsumer($connection)
+    {
+    	if (isset($this->watcher[$connection->id]) && !isset($this->consumer[$connection->id])) {
+    		$this->consumer[$connection->id] = $connection;
+	    }
+	    $this->dispatch();
+    }
+
+    public function enqueue($data)
+    {
+    	$this->queue->enqueue($data);
+    	$this->dispatch();
+    }
+
+    private function dispatch()
+    {
+    	if ($this->queue->isEmpty() || count($this->consumer) == 0) {
+    		return;
+	    }
+
+		while (!$this->queue->isEmpty()) {
+    		$data = $this->queue->dequeue();
+    		$idx = key($this->consumer);
+    		$connection = $this->consumer[$idx];
+    		unset($this->consumer[$idx]);
+	        $connection->send(serialize(array('type'=>'queue', 'channel'=>$this->name, 'data' => $data)));
+	        if (count($this->consumer) == 0) {
+		        break;
+	        }
+		}
+    }
+
+    public function isEmpty()
+    {
+        return empty($this->watcher) && $this->queue->isEmpty();
+    }
+
+}
\ No newline at end of file
diff --git a/vendor/workerman/channel/src/Server.php b/vendor/workerman/channel/src/Server.php
new file mode 100644
index 0000000..2363083
--- /dev/null
+++ b/vendor/workerman/channel/src/Server.php
@@ -0,0 +1,163 @@
+<?php
+namespace Channel;
+
+use Workerman\Protocols\Frame;
+use Workerman\Worker;
+
+/**
+ * Channel server.
+ */
+class Server
+{
+    /**
+     * Worker instance.
+     * @var Worker
+     */
+    protected $_worker = null;
+
+    /**
+     * Queues
+     * @var Queue[]
+     */
+    protected $_queues = array();
+
+    private $ip;
+
+    /**
+     * Construct.
+     * @param string $ip Bind ip address or unix domain socket.
+     * Bind unix domain socket use 'unix:///tmp/channel.sock'
+     * @param int $port Tcp port to bind, only used when listen on tcp.
+     */
+    public function __construct($ip = '0.0.0.0', $port = 2206)
+    {
+        if (strpos($ip, 'unix:') === false) {
+            $worker = new Worker("frame://$ip:$port");
+        } else {
+            $worker = new Worker($ip);
+            $worker->protocol = Frame::class;
+        }
+        $this->ip = $ip;
+        $worker->count = 1;
+        $worker->name = 'ChannelServer';
+        $worker->channels = array();
+        $worker->onMessage = array($this, 'onMessage') ;
+        $worker->onClose = array($this, 'onClose');
+        $this->_worker = $worker;
+    }
+
+    /**
+     * onClose
+     * @return void
+     */
+    public function onClose($connection)
+    {
+        if (!empty($connection->channels)) {
+	        foreach ($connection->channels as $channel) {
+		        unset($this->_worker->channels[$channel][$connection->id]);
+		        if (empty($this->_worker->channels[$channel])) {
+			        unset($this->_worker->channels[$channel]);
+		        }
+	        }
+        }
+
+        if (!empty($connection->watchs)) {
+        	foreach ($connection->watchs as $channel) {
+        		if (isset($this->_queues[$channel])) {
+        			$this->_queues[$channel]->removeWatch($connection);
+        			if ($this->_queues[$channel]->isEmpty()) {
+        				unset($this->_queues[$channel]);
+			        }
+		        }
+	        }
+        }
+    }
+
+    /**
+     * onMessage.
+     * @param \Workerman\Connection\TcpConnection $connection
+     * @param string $data
+     */
+    public function onMessage($connection, $data)
+    {
+        if(!$data)
+        {
+            return;
+        }
+        $worker = $this->_worker;
+        $data = unserialize($data);
+        $type = $data['type'];
+        switch($type)
+        {
+            case 'subscribe':
+                foreach($data['channels'] as $channel)
+                {
+                    $connection->channels[$channel] = $channel;
+                    $worker->channels[$channel][$connection->id] = $connection;
+                }
+                break;
+            case 'unsubscribe':
+                foreach($data['channels'] as $channel) {
+                    if (isset($connection->channels[$channel])) {
+                        unset($connection->channels[$channel]);
+                    }
+                    if (isset($worker->channels[$channel][$connection->id])) {
+                        unset($worker->channels[$channel][$connection->id]);
+                        if (empty($worker->channels[$channel])) {
+                            unset($worker->channels[$channel]);
+                        }
+                    }
+                }
+                break;
+            case 'publish':
+                foreach ($data['channels'] as $channel) {
+                    if (empty($worker->channels[$channel])) {
+                        continue;
+                    }
+                    $buffer = serialize(array('type' => 'event', 'channel' => $channel, 'data' => $data['data']))."\n";
+                    foreach ($worker->channels[$channel] as $connection) {
+                        $connection->send($buffer);
+                    }
+                }
+                break;
+            case 'watch':
+            	foreach ($data['channels'] as $channel) {
+		            $this->getQueue($channel)->addWatch($connection);
+	            }
+                break;
+            case 'unwatch':
+	            foreach ($data['channels'] as $channel) {
+		            if (isset($this->_queues[$channel])) {
+			            $this->_queues[$channel]->removeWatch($connection);
+			            if ($this->_queues[$channel]->isEmpty()) {
+				            unset($this->_queues[$channel]);
+			            }
+		            }
+	            }
+                break;
+            case 'enqueue':
+            	foreach ($data['channels'] as $channel) {
+		            $this->getQueue($channel)->enqueue($data['data']);
+	            }
+                break;
+            case 'reserve':
+				if (isset($connection->watchs)) {
+					foreach ($connection->watchs as $channel) {
+						if (isset($this->_queues[$channel])) {
+							$this->_queues[$channel]->addConsumer($connection);
+						}
+					}
+				}
+                break;
+        }
+    }
+
+    private function getQueue($channel)
+    {
+        if (isset($this->_queues[$channel])) {
+            return $this->_queues[$channel];
+        }
+        return ($this->_queues[$channel] = new Queue($channel));
+    }
+
+}
diff --git a/vendor/workerman/channel/test/queue.php b/vendor/workerman/channel/test/queue.php
new file mode 100644
index 0000000..cce6ae3
--- /dev/null
+++ b/vendor/workerman/channel/test/queue.php
@@ -0,0 +1,53 @@
+<?php
+
+use Channel\Client;
+use Channel\Server;
+use Workerman\Worker;
+use Workerman\Lib\Timer;
+
+// composer autoload
+include __DIR__ . '/../vendor/autoload.php';
+
+$channel_server = new Server();
+
+$worker = new Worker();
+$worker->name = 'Event';
+$worker->onWorkerStart = function()
+{
+	Client::connect();
+
+	$count = 0;
+	$timerId = Timer::add(0.01, function() use (&$timerId, &$count) {
+		Client::publish('test event', 'some data');
+		$count++;
+		Client::enqueue('task-queue', time());
+		if ($count == 1000) {
+			Timer::del($timerId);
+		}
+	});
+
+	Timer::add(10, function() {
+		Client::enqueue('task-queue', 'hello every 10 seconds');
+	});
+};
+
+$mq = new Worker();
+$mq->name = 'Queue';
+$mq->count = 4;
+$mq->onWorkerStart = function($worker) {
+	Client::connect();
+	$countDown = 20;
+	$id = 1;
+	Client::watch('task-queue', function($data) use ($worker, &$countDown, &$id) {
+		echo "[$id] Worker {$worker->id} get queue: $data\n";
+		sleep(0.2);
+		$countDown--;
+		$id++;
+		if ($worker->id > 1 && $countDown == 0) {
+			Client::unwatch('task-queue');
+		}
+		Timer::add(1, [Client::class, 'reserve'], [], false);
+	});
+};
+
+Worker::runAll();
diff --git a/vendor/workerman/channel/test/server.php b/vendor/workerman/channel/test/server.php
new file mode 100644
index 0000000..6e652f0
--- /dev/null
+++ b/vendor/workerman/channel/test/server.php
@@ -0,0 +1,28 @@
+<?php
+
+use Channel\Client;
+use Channel\Server;
+use Workerman\Worker;
+use Workerman\Lib\Timer;
+
+// composer autoload
+include __DIR__ . '/../vendor/autoload.php';
+
+$channel_server = new Server();
+
+$worker = new Worker();
+$worker->onWorkerStart = function()
+{
+    Client::connect();
+
+    Client::on('test event', function($event_data){
+        echo 'test event triggered event_data :';
+        var_dump($event_data);
+    });
+
+    Timer::add(2, function(){
+        Client::publish('test event', 'some data');
+    });
+};
+
+Worker::runAll();
diff --git a/vendor/workerman/phpsocket.io/.github/FUNDING.yml b/vendor/workerman/phpsocket.io/.github/FUNDING.yml
new file mode 100644
index 0000000..3d88b8d
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/.github/FUNDING.yml
@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+
+open_collective: walkor
+patreon: walkor
diff --git a/vendor/workerman/phpsocket.io/.gitignore b/vendor/workerman/phpsocket.io/.gitignore
new file mode 100644
index 0000000..afb6dd6
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/.gitignore
@@ -0,0 +1,6 @@
+.buildpath
+.project
+.settings/org.eclipse.php.core.prefs
+vendor
+examples/vendor
+composer.lock
diff --git a/vendor/workerman/phpsocket.io/README.md b/vendor/workerman/phpsocket.io/README.md
new file mode 100644
index 0000000..86546fc
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/README.md
@@ -0,0 +1,162 @@
+# phpsocket.io
+A server side alternative implementation of [socket.io](https://github.com/socketio/socket.io) in PHP based on [Workerman](https://github.com/walkor/Workerman).<br>
+
+# Notice
+Only support socket.io v1.3.0 or greater. <br>
+This project is just translate socket.io by [workerman](https://github.com/walkor/Workerman).<br>
+More api just see http://socket.io/docs/server-api/ 
+
+# Install
+composer require workerman/phpsocket.io
+
+# Examples
+## Simple chat
+start.php
+```php
+
+use Workerman\Worker;
+use PHPSocketIO\SocketIO;
+require_once __DIR__ . '/vendor/autoload.php';
+
+// Listen port 2021 for socket.io client
+$io = new SocketIO(2021);
+$io->on('connection', function ($socket) use ($io) {
+    $socket->on('chat message', function ($msg) use ($io) {
+        $io->emit('chat message', $msg);
+    });
+});
+
+Worker::runAll();
+```
+
+## Another chat demo
+
+https://github.com/walkor/phpsocket.io/blob/master/examples/chat/start_io.php
+```php
+
+use Workerman\Worker;
+use PHPSocketIO\SocketIO;
+require_once __DIR__ . '/vendor/autoload.php';
+
+// Listen port 2020 for socket.io client
+$io = new SocketIO(2020);
+$io->on('connection', function ($socket) {
+    $socket->addedUser = false;
+
+    // When the client emits 'new message', this listens and executes
+    $socket->on('new message', function ($data) use ($socket) {
+        // We tell the client to execute 'new message'
+        $socket->broadcast->emit('new message', array(
+            'username' => $socket->username,
+            'message' => $data
+        ));
+    });
+
+    // When the client emits 'add user', this listens and executes
+    $socket->on('add user', function ($username) use ($socket) {
+        global $usernames, $numUsers;
+
+        // We store the username in the socket session for this client
+        $socket->username = $username;
+        // Add the client's username to the global list
+        $usernames[$username] = $username;
+        ++$numUsers;
+
+        $socket->addedUser = true;
+        $socket->emit('login', array( 
+            'numUsers' => $numUsers
+        ));
+
+        // echo globally (all clients) that a person has connected
+        $socket->broadcast->emit('user joined', array(
+            'username' => $socket->username,
+            'numUsers' => $numUsers
+        ));
+    });
+
+    // When the client emits 'typing', we broadcast it to others
+    $socket->on('typing', function () use ($socket) {
+        $socket->broadcast->emit('typing', array(
+            'username' => $socket->username
+        ));
+    });
+
+    // When the client emits 'stop typing', we broadcast it to others
+    $socket->on('stop typing', function () use ($socket) {
+        $socket->broadcast->emit('stop typing', array(
+            'username' => $socket->username
+        ));
+    });
+
+    // When the user disconnects, perform this
+    $socket->on('disconnect', function () use ($socket) {
+        global $usernames, $numUsers;
+
+        // Remove the username from global usernames list
+        if ($socket->addedUser) {
+            unset($usernames[$socket->username]);
+            --$numUsers;
+
+            // echo globally that this client has left
+            $socket->broadcast->emit('user left', array(
+               'username' => $socket->username,
+               'numUsers' => $numUsers
+            ));
+        }
+   });
+});
+
+Worker::runAll();
+```
+
+## Enable SSL for https 
+**```(phpsocket.io>=1.1.1 && workerman>=3.3.7 required)```**
+
+start.php
+```php
+<?php
+
+use Workerman\Worker;
+use PHPSocketIO\SocketIO;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+// SSL context
+$context = array(
+    'ssl' => array(
+        'local_cert'  => '/your/path/of/server.pem',
+        'local_pk'    => '/your/path/of/server.key',
+        'verify_peer' => false
+    )
+);
+$io = new SocketIO(2021, $context);
+
+$io->on('connection', function ($connection) use ($io) {
+    echo "New connection coming\n";
+});
+
+Worker::runAll();
+```
+
+# 手册
+[中文手册](https://github.com/walkor/phpsocket.io/tree/master/docs/zh)
+
+# Livedemo
+[chat demo](http://demos.workerman.net/phpsocketio-chat/)
+
+# Run chat example
+cd examples/chat
+
+## Start
+```php start.php start``` for debug mode
+
+```php start.php start -d ``` for daemon mode
+
+## Stop
+```php start.php stop```
+
+## Status
+```php start.php status```
+
+# License
+MIT
diff --git a/vendor/workerman/phpsocket.io/composer.json b/vendor/workerman/phpsocket.io/composer.json
new file mode 100644
index 0000000..23dcb18
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/composer.json
@@ -0,0 +1,14 @@
+{
+    "name"  : "workerman/phpsocket.io",
+    "type"  : "library",
+    "keywords": ["socket.io"],
+    "homepage": "http://www.workerman.net",
+    "license" : "MIT",
+    "require": {
+        "workerman/workerman" : ">=3.5.16",
+        "workerman/channel" : ">=1.0.0"
+    },
+    "autoload": {
+        "psr-4": {"PHPSocketIO\\": "./src"}
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/docs/README.md b/vendor/workerman/phpsocket.io/docs/README.md
new file mode 100644
index 0000000..69ba49b
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/docs/README.md
@@ -0,0 +1,3 @@
+## Documentation
+
+[中文](./zh/)
diff --git a/vendor/workerman/phpsocket.io/docs/zh/README.md b/vendor/workerman/phpsocket.io/docs/zh/README.md
new file mode 100644
index 0000000..5f2776d
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/docs/zh/README.md
@@ -0,0 +1,259 @@
+# phpsocket.io手册
+
+## 安装
+请使用composer集成phpsocket.io。
+
+脚本中引用vendor中的autoload.php实现SocketIO相关类的加载。例如
+```php
+require_once '/你的vendor路径/autoload.php';
+```
+
+## 服务端和客户端连接
+**创建一个SocketIO服务端**
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+use PHPSocketIO\SocketIO;
+
+// 创建socket.io服务端,监听3120端口
+$io = new SocketIO(3120);
+// 当有客户端连接时打印一行文字
+$io->on('connection', function($socket)use($io){
+  echo "new connection coming\n";
+});
+
+Worker::runAll();
+```
+**客户端**
+```javascript
+<script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script>
+<script>
+// 如果服务端不在本机,请把127.0.0.1改成服务端ip
+var socket = io('http://127.0.0.1:3120');
+// 当连接服务端成功时触发connect默认事件
+socket.on('connect', function(){
+    console.log('connect success');
+});
+</script>
+```
+
+## 自定义事件
+socket.io主要是通过事件来进行通讯交互的。
+
+socket连接除了自带的connect,message,disconnect三个事件以外,在服务端和客户端开发者可以自定义其它事件。
+
+服务端和客户端都通过emit方法触发对端的事件。
+
+例如下面的代码在服务端定义了一个```chat message```事件,事件参数为```$msg```。
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+use PHPSocketIO\SocketIO;
+
+$io = new SocketIO(3120);
+// 当有客户端连接时
+$io->on('connection', function($socket)use($io){
+  // 定义chat message事件回调函数
+  $socket->on('chat message', function($msg)use($io){
+    // 触发所有客户端定义的chat message from server事件
+    $io->emit('chat message from server', $msg);
+  });
+});
+Worker::runAll();
+```
+
+客户端通过下面的方法触发服务端的chat message事件。
+```javascript
+<script src='//cdn.bootcss.com/socket.io/1.3.7/socket.io.js'></script>
+<script>
+// 连接服务端
+var socket = io('http://127.0.0.1:3120');
+// 触发服务端的chat message事件
+socket.emit('chat message', '这个是消息内容...');
+// 服务端通过emit('chat message from server', $msg)触发客户端的chat message from server事件
+socket.on('chat message from server', function(msg){
+    console.log('get message:' + msg + ' from server');
+});
+</script>
+```
+
+## workerStart事件
+phpsocket.io提供了workerStart事件回调,也就是当进程启动后准备好接受客户端链接时触发的回调。
+一个进程生命周期只会触发一次。可以在这里设置一些全局的事情,比如开一个新的Worker端口等等。
+```php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+use PHPSocketIO\SocketIO;
+
+$io = new SocketIO(9120);
+
+// 监听一个http端口,通过http协议访问这个端口可以向所有客户端推送数据(url类似http://ip:9191?msg=xxxx)
+$io->on('workerStart', function()use($io) {
+    $inner_http_worker = new Worker('http://0.0.0.0:9191');
+    $inner_http_worker->onMessage = function($http_connection, $data)use($io){
+        if(!isset($_GET['msg'])) {
+            return $http_connection->send('fail, $_GET["msg"] not found');
+        }
+        $io->emit('chat message', $_GET['msg']);
+        $http_connection->send('ok');
+    };
+    $inner_http_worker->listen();
+});
+
+// 当有客户端连接时
+$io->on('connection', function($socket)use($io){
+  // 定义chat message事件回调函数
+  $socket->on('chat message', function($msg)use($io){
+    // 触发所有客户端定义的chat message from server事件
+    $io->emit('chat message from server', $msg);
+  });
+});
+
+Worker::runAll();
+```
+phpsocket.io启动后开内部http端口通过phpsocket.io向客户端推送数据参考 [web-msg-sender](http://www.workerman.net/web-sender)。
+
+## 分组
+socket.io提供分组功能,允许向某个分组发送事件,例如向某个房间广播数据。
+
+1、加入分组(一个连接可以加入多个分组)
+```php
+$socket->join('group name');
+```
+2、离开分组(连接断开时会自动从分组中离开)
+```php
+$socket->leave('group name');
+```
+
+## 向客户端发送事件的各种方法
+$io是SocketIO对象。$socket是客户端连接
+
+$data可以是数字和字符串,也可以是数组。当$data是数组时,客户端会自动转换为javascript对象。
+
+同理如果客户端向服务端emit某个事件传递的是一个javascript对象,在服务端接收时会自动转换为php数组。
+
+1、向当前客户端发送事件
+```php
+$socket->emit('event name', $data);
+```
+2、向所有客户端发送事件
+```php
+$io->emit('event name', $data);
+```
+3、向所有客户端发送事件,但不包括当前连接。
+```php
+$socket->broadcast->emit('event name', $data);
+```
+
+4、向某个分组的所有客户端发送事件
+```php
+$io->to('group name')->emit('event name', $data);
+```
+
+## 获取客户端ip
+```php
+$io->on('connection', function($socket)use($io){
+        var_dump($socket->conn->remoteAddress);
+});
+```
+
+## 关闭链接
+```php
+$socket->disconnect();
+```
+
+## 限制连接域名
+当我们想指定特定域名的页面才能连接,可以用$io->origins方法来设置域名白名单。
+```php
+$io = new SocketIO(2020);
+$io->origins('http://example.com:8080');
+```
+多个域名时用空格分隔,类似
+```php
+$io = new SocketIO(2020);
+$io->origins('http://workerman.net http://www.workerman.net');
+```
+
+## 支持SSL(https wss)
+SSL支持有两种方法,workerman原生和nginx代理
+### workerman原生支持
+SSL 要求workerman>=3.3.7 phpsocket.io>=1.1.1
+
+```php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+use Workerman\Worker;
+use PHPSocketIO\SocketIO;
+
+// 传入ssl选项,包含证书的路径
+$context = array(
+    'ssl' => array(
+        'local_cert'  => '/your/path/of/server.pem',
+        'local_pk'    => '/your/path/of/server.key',
+        'verify_peer' => false,
+    )
+);
+$io = new SocketIO(2120, $context);
+
+$io->on('connection', function($socket)use($io){
+  echo "new connection coming\n";
+});
+
+Worker::runAll();
+```
+**注意:**<br>
+1、证书是要验证域名的,所以客户端链接时要指定域名才能顺利的建立链接。<br>
+2、客户端连接时不能再用http方式,要改成https类似下面这样。
+```javascript
+<script>
+var socket = io('https://yoursite.com:2120');
+//.....
+</script>
+```
+### nginx代理SSL
+
+**前提条件及准备工作:**
+
+1、已经安装nginx,版本不低于1.3
+
+2、假设phpsocket.io监听的是2120端口
+
+3、已经申请了证书(pem/crt文件及key文件)放在了/etc/nginx/conf.d/ssl下
+
+4、打算利用nginx开启443端口对外提供ssl代理服务(端口可以根据需要修改)
+
+**nginx配置类似如下:**
+```
+server {
+  listen 443;
+
+  ssl on;
+  ssl_certificate /etc/ssl/server.pem;
+  ssl_certificate_key /etc/ssl/server.key;
+  ssl_session_timeout 5m;
+  ssl_session_cache shared:SSL:50m;
+  ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
+  ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
+
+  location /socket.io
+  {
+    proxy_pass http://127.0.0.1:2120;
+    proxy_http_version 1.1;
+    proxy_set_header Upgrade $http_upgrade;
+    proxy_set_header Connection "Upgrade";
+    proxy_set_header X-Real-IP $remote_addr;
+  }
+
+  # location / {} 站点的其它配置...
+}
+```
+**注意:**<br>
+1、证书是要验证域名的,所以客户端链接时要指定域名才能顺利的建立链接。<br>
+2、客户端连接时不能再用http方式,要改成https类似下面这样。
+```javascript
+<script>
+var socket = io('https://yoursite.com');
+//.....
+</scrip
diff --git a/vendor/workerman/phpsocket.io/examples/chat/README.md b/vendor/workerman/phpsocket.io/examples/chat/README.md
new file mode 100644
index 0000000..edd8565
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/README.md
@@ -0,0 +1,20 @@
+# For chat demo
+## start
+```php start.php start``` for debug mode
+
+```php start.php start -d``` for daemon mode
+
+## stop
+```php start.php stop```
+
+## satus 
+```php start.php status```
+
+## restart
+``` php start.php restart```
+
+## reload
+``` php start.php reload```
+
+## connections
+``` php start.php connections```
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/index.html b/vendor/workerman/phpsocket.io/examples/chat/public/index.html
new file mode 100644
index 0000000..5a05580
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Socket.IO Chat Example</title>
+  <link rel="stylesheet" href="style.css">
+</head>
+<body>
+  <ul class="pages">
+    <li class="chat page">
+      <div class="chatArea">
+        <ul class="messages"></ul>
+      </div>
+      <input class="inputMessage" placeholder="Type here..."/>
+    </li>
+    <li class="login page">
+      <div class="form">
+        <h3 class="title">What's your nickname?</h3>
+        <input class="usernameInput" type="text" maxlength="14" />
+      </div>
+    </li>
+  </ul>
+
+  <script src="/jquery.min.js"></script>
+  <script src="/socket.io-client/socket.io.js"></script>
+  <script src="/main.js"></script>
+</body>
+</html>
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/jquery.min.js b/vendor/workerman/phpsocket.io/examples/chat/public/jquery.min.js
new file mode 100644
index 0000000..ab28a24
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/jquery.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
+if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
+},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/main.js b/vendor/workerman/phpsocket.io/examples/chat/public/main.js
new file mode 100644
index 0000000..36abe69
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/main.js
@@ -0,0 +1,282 @@
+$(function() {
+  var FADE_TIME = 150; // ms
+  var TYPING_TIMER_LENGTH = 400; // ms
+  var COLORS = [
+    '#e21400', '#91580f', '#f8a700', '#f78b00',
+    '#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
+    '#3b88eb', '#3824aa', '#a700ff', '#d300e7'
+  ];
+
+  // Initialize variables
+  var $window = $(window);
+  var $usernameInput = $('.usernameInput'); // Input for username
+  var $messages = $('.messages'); // Messages area
+  var $inputMessage = $('.inputMessage'); // Input message input box
+
+  var $loginPage = $('.login.page'); // The login page
+  var $chatPage = $('.chat.page'); // The chatroom page
+
+  // Prompt for setting a username
+  var username;
+  var connected = false;
+  var typing = false;
+  var lastTypingTime;
+  var $currentInput = $usernameInput.focus();
+
+  var socket = io('http://'+document.domain+':2020');
+
+  const addParticipantsMessage = (data) => {
+    var message = '';
+    if (data.numUsers === 1) {
+      message += "there's 1 participant";
+    } else {
+      message += "there are " + data.numUsers + " participants";
+    }
+    log(message);
+  }
+
+  // Sets the client's username
+  const setUsername = () => {
+    username = cleanInput($usernameInput.val().trim());
+
+    // If the username is valid
+    if (username) {
+      $loginPage.fadeOut();
+      $chatPage.show();
+      $loginPage.off('click');
+      $currentInput = $inputMessage.focus();
+
+      // Tell the server your username
+      socket.emit('add user', username);
+    }
+  }
+
+  // Sends a chat message
+  const sendMessage = () => {
+    var message = $inputMessage.val();
+    // Prevent markup from being injected into the message
+    message = cleanInput(message);
+    // if there is a non-empty message and a socket connection
+    if (message && connected) {
+      $inputMessage.val('');
+      addChatMessage({
+        username: username,
+        message: message
+      });
+      // tell server to execute 'new message' and send along one parameter
+      socket.emit('new message', message);
+    }
+  }
+
+  // Log a message
+  const log = (message, options) => {
+    var $el = $('<li>').addClass('log').text(message);
+    addMessageElement($el, options);
+  }
+
+  // Adds the visual chat message to the message list
+  const addChatMessage = (data, options) => {
+    // Don't fade the message in if there is an 'X was typing'
+    var $typingMessages = getTypingMessages(data);
+    options = options || {};
+    if ($typingMessages.length !== 0) {
+      options.fade = false;
+      $typingMessages.remove();
+    }
+
+    var $usernameDiv = $('<span class="username"/>')
+      .text(data.username)
+      .css('color', getUsernameColor(data.username));
+    var $messageBodyDiv = $('<span class="messageBody">')
+      .text(data.message);
+
+    var typingClass = data.typing ? 'typing' : '';
+    var $messageDiv = $('<li class="message"/>')
+      .data('username', data.username)
+      .addClass(typingClass)
+      .append($usernameDiv, $messageBodyDiv);
+
+    addMessageElement($messageDiv, options);
+  }
+
+  // Adds the visual chat typing message
+  const addChatTyping = (data) => {
+    data.typing = true;
+    data.message = 'is typing';
+    addChatMessage(data);
+  }
+
+  // Removes the visual chat typing message
+  const removeChatTyping = (data) => {
+    getTypingMessages(data).fadeOut(function () {
+      $(this).remove();
+    });
+  }
+
+  // Adds a message element to the messages and scrolls to the bottom
+  // el - The element to add as a message
+  // options.fade - If the element should fade-in (default = true)
+  // options.prepend - If the element should prepend
+  //   all other messages (default = false)
+  const addMessageElement = (el, options) => {
+    var $el = $(el);
+
+    // Setup default options
+    if (!options) {
+      options = {};
+    }
+    if (typeof options.fade === 'undefined') {
+      options.fade = true;
+    }
+    if (typeof options.prepend === 'undefined') {
+      options.prepend = false;
+    }
+
+    // Apply options
+    if (options.fade) {
+      $el.hide().fadeIn(FADE_TIME);
+    }
+    if (options.prepend) {
+      $messages.prepend($el);
+    } else {
+      $messages.append($el);
+    }
+    $messages[0].scrollTop = $messages[0].scrollHeight;
+  }
+
+  // Prevents input from having injected markup
+  const cleanInput = (input) => {    
+    return $('<div/>').text(input).html();
+  }
+
+  // Updates the typing event
+  const updateTyping = () => {
+    if (connected) {
+      if (!typing) {
+        typing = true;
+        socket.emit('typing');
+      }
+      lastTypingTime = (new Date()).getTime();
+
+    setTimeout(() => {
+        var typingTimer = (new Date()).getTime();
+        var timeDiff = typingTimer - lastTypingTime;
+        if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
+          socket.emit('stop typing');
+          typing = false;
+        }
+      }, TYPING_TIMER_LENGTH);
+    }
+  }
+
+  // Gets the 'X is typing' messages of a user
+  const getTypingMessages = (data) => {
+    return $('.typing.message').filter(function (i) {
+      return $(this).data('username') === data.username;
+    });
+  }
+
+  // Gets the color of a username through our hash function
+  const getUsernameColor = (username) => {
+    // Compute hash code
+    var hash = 7;
+    for (var i = 0; i < username.length; i++) {
+       hash = username.charCodeAt(i) + (hash << 5) - hash;
+    }
+    // Calculate color
+    var index = Math.abs(hash % COLORS.length);
+    return COLORS[index];
+  }
+
+  // Keyboard events
+
+  $window.keydown(event => {
+    // Auto-focus the current input when a key is typed
+    if (!(event.ctrlKey || event.metaKey || event.altKey)) {
+      $currentInput.focus();
+    }
+    // When the client hits ENTER on their keyboard
+    if (event.which === 13) {
+      if (username) {
+        sendMessage();
+        socket.emit('stop typing');
+        typing = false;
+      } else {
+        setUsername();
+      }
+    }
+  });
+
+  $inputMessage.on('input', () => {
+    updateTyping();
+  });
+
+  // Click events
+
+  // Focus input when clicking anywhere on login page
+  $loginPage.click(() => {
+    $currentInput.focus();
+  });
+
+  // Focus input when clicking on the message input's border
+  $inputMessage.click(() => {
+    $inputMessage.focus();
+  });
+
+  // Socket events
+
+  // Whenever the server emits 'login', log the login message
+  socket.on('login', (data) => {
+    connected = true;
+    // Display the welcome message
+    var message = "Welcome to Socket.IO Chat – ";
+    log(message, {
+      prepend: true
+    });
+    addParticipantsMessage(data);
+  });
+
+  // Whenever the server emits 'new message', update the chat body
+  socket.on('new message', (data) => {
+    addChatMessage(data);
+  });
+
+  // Whenever the server emits 'user joined', log it in the chat body
+  socket.on('user joined', (data) => {
+    log(data.username + ' joined');
+    addParticipantsMessage(data);
+  });
+
+  // Whenever the server emits 'user left', log it in the chat body
+  socket.on('user left', (data) => {
+    log(data.username + ' left');
+    addParticipantsMessage(data);
+    removeChatTyping(data);
+  });
+
+  // Whenever the server emits 'typing', show the typing message
+  socket.on('typing', (data) => {
+    addChatTyping(data);
+  });
+
+  // Whenever the server emits 'stop typing', kill the typing message
+  socket.on('stop typing', (data) => {
+    removeChatTyping(data);
+  });
+
+  socket.on('disconnect', () => {
+  log('you have been disconnected');
+  });
+
+  socket.on('reconnect', () => {
+  log('you have been reconnected');
+  if (username) {
+    socket.emit('add user', username);
+  }
+  });
+
+  socket.on('reconnect_error', () => {
+  log('attempt to reconnect has failed');
+  });
+  
+});
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/LICENSE b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/LICENSE
new file mode 100644
index 0000000..9338df1
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Guillermo Rauch
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/index.js b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/index.js
new file mode 100644
index 0000000..91887e4
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/index.js
@@ -0,0 +1,92 @@
+
+/**
+ * Module dependencies.
+ */
+
+var url = require('./url');
+var parser = require('socket.io-parser');
+var Manager = require('./manager');
+var debug = require('debug')('socket.io-client');
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = lookup;
+
+/**
+ * Managers cache.
+ */
+
+var cache = exports.managers = {};
+
+/**
+ * Looks up an existing `Manager` for multiplexing.
+ * If the user summons:
+ *
+ *   `io('http://localhost/a');`
+ *   `io('http://localhost/b');`
+ *
+ * We reuse the existing instance based on same scheme/port/host,
+ * and we initialize sockets for each namespace.
+ *
+ * @api public
+ */
+
+function lookup(uri, opts) {
+  if (typeof uri == 'object') {
+    opts = uri;
+    uri = undefined;
+  }
+
+  opts = opts || {};
+
+  var parsed = url(uri);
+  var source = parsed.source;
+  var id = parsed.id;
+  var path = parsed.path;
+  var sameNamespace = cache[id] && path in cache[id].nsps;
+  var newConnection = opts.forceNew || opts['force new connection'] ||
+                      false === opts.multiplex || sameNamespace;
+
+  var io;
+
+  if (newConnection) {
+    debug('ignoring socket cache for %s', source);
+    io = Manager(source, opts);
+  } else {
+    if (!cache[id]) {
+      debug('new io instance for %s', source);
+      cache[id] = Manager(source, opts);
+    }
+    io = cache[id];
+  }
+
+  return io.socket(parsed.path);
+}
+
+/**
+ * Protocol version.
+ *
+ * @api public
+ */
+
+exports.protocol = parser.protocol;
+
+/**
+ * `connect`.
+ *
+ * @param {String} uri
+ * @api public
+ */
+
+exports.connect = lookup;
+
+/**
+ * Expose constructors for standalone build.
+ *
+ * @api public
+ */
+
+exports.Manager = require('./manager');
+exports.Socket = require('./socket');
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/manager.js b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/manager.js
new file mode 100644
index 0000000..da8376f
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/manager.js
@@ -0,0 +1,539 @@
+
+/**
+ * Module dependencies.
+ */
+
+var url = require('./url');
+var eio = require('engine.io-client');
+var Socket = require('./socket');
+var Emitter = require('component-emitter');
+var parser = require('socket.io-parser');
+var on = require('./on');
+var bind = require('component-bind');
+var object = require('object-component');
+var debug = require('debug')('socket.io-client:manager');
+var indexOf = require('indexof');
+var Backoff = require('backo2');
+
+/**
+ * Module exports
+ */
+
+module.exports = Manager;
+
+/**
+ * `Manager` constructor.
+ *
+ * @param {String} engine instance or engine uri/opts
+ * @param {Object} options
+ * @api public
+ */
+
+function Manager(uri, opts){
+  if (!(this instanceof Manager)) return new Manager(uri, opts);
+  if (uri && ('object' == typeof uri)) {
+    opts = uri;
+    uri = undefined;
+  }
+  opts = opts || {};
+
+  opts.path = opts.path || '/socket.io';
+  this.nsps = {};
+  this.subs = [];
+  this.opts = opts;
+  this.reconnection(opts.reconnection !== false);
+  this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
+  this.reconnectionDelay(opts.reconnectionDelay || 1000);
+  this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
+  this.randomizationFactor(opts.randomizationFactor || 0.5);
+  this.backoff = new Backoff({
+    min: this.reconnectionDelay(),
+    max: this.reconnectionDelayMax(),
+    jitter: this.randomizationFactor()
+  });
+  this.timeout(null == opts.timeout ? 20000 : opts.timeout);
+  this.readyState = 'closed';
+  this.uri = uri;
+  this.connected = [];
+  this.lastPing = null;
+  this.encoding = false;
+  this.packetBuffer = [];
+  this.encoder = new parser.Encoder();
+  this.decoder = new parser.Decoder();
+  this.autoConnect = opts.autoConnect !== false;
+  if (this.autoConnect) this.open();
+}
+
+/**
+ * Propagate given event to sockets and emit on `this`
+ *
+ * @api private
+ */
+
+Manager.prototype.emitAll = function() {
+  this.emit.apply(this, arguments);
+  for (var nsp in this.nsps) {
+    this.nsps[nsp].emit.apply(this.nsps[nsp], arguments);
+  }
+};
+
+/**
+ * Update `socket.id` of all sockets
+ *
+ * @api private
+ */
+
+Manager.prototype.updateSocketIds = function(){
+  for (var nsp in this.nsps) {
+    this.nsps[nsp].id = this.engine.id;
+  }
+};
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Manager.prototype);
+
+/**
+ * Sets the `reconnection` config.
+ *
+ * @param {Boolean} true/false if it should automatically reconnect
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnection = function(v){
+  if (!arguments.length) return this._reconnection;
+  this._reconnection = !!v;
+  return this;
+};
+
+/**
+ * Sets the reconnection attempts config.
+ *
+ * @param {Number} max reconnection attempts before giving up
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnectionAttempts = function(v){
+  if (!arguments.length) return this._reconnectionAttempts;
+  this._reconnectionAttempts = v;
+  return this;
+};
+
+/**
+ * Sets the delay between reconnections.
+ *
+ * @param {Number} delay
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnectionDelay = function(v){
+  if (!arguments.length) return this._reconnectionDelay;
+  this._reconnectionDelay = v;
+  this.backoff && this.backoff.setMin(v);
+  return this;
+};
+
+Manager.prototype.randomizationFactor = function(v){
+  if (!arguments.length) return this._randomizationFactor;
+  this._randomizationFactor = v;
+  this.backoff && this.backoff.setJitter(v);
+  return this;
+};
+
+/**
+ * Sets the maximum delay between reconnections.
+ *
+ * @param {Number} delay
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnectionDelayMax = function(v){
+  if (!arguments.length) return this._reconnectionDelayMax;
+  this._reconnectionDelayMax = v;
+  this.backoff && this.backoff.setMax(v);
+  return this;
+};
+
+/**
+ * Sets the connection timeout. `false` to disable
+ *
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.timeout = function(v){
+  if (!arguments.length) return this._timeout;
+  this._timeout = v;
+  return this;
+};
+
+/**
+ * Starts trying to reconnect if reconnection is enabled and we have not
+ * started reconnecting yet
+ *
+ * @api private
+ */
+
+Manager.prototype.maybeReconnectOnOpen = function() {
+  // Only try to reconnect if it's the first time we're connecting
+  if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) {
+    // keeps reconnection from firing twice for the same reconnection loop
+    this.reconnect();
+  }
+};
+
+
+/**
+ * Sets the current transport `socket`.
+ *
+ * @param {Function} optional, callback
+ * @return {Manager} self
+ * @api public
+ */
+
+Manager.prototype.open =
+Manager.prototype.connect = function(fn){
+  debug('readyState %s', this.readyState);
+  if (~this.readyState.indexOf('open')) return this;
+
+  debug('opening %s', this.uri);
+  this.engine = eio(this.uri, this.opts);
+  var socket = this.engine;
+  var self = this;
+  this.readyState = 'opening';
+  this.skipReconnect = false;
+
+  // emit `open`
+  var openSub = on(socket, 'open', function() {
+    self.onopen();
+    fn && fn();
+  });
+
+  // emit `connect_error`
+  var errorSub = on(socket, 'error', function(data){
+    debug('connect_error');
+    self.cleanup();
+    self.readyState = 'closed';
+    self.emitAll('connect_error', data);
+    if (fn) {
+      var err = new Error('Connection error');
+      err.data = data;
+      fn(err);
+    } else {
+      // Only do this if there is no fn to handle the error
+      self.maybeReconnectOnOpen();
+    }
+  });
+
+  // emit `connect_timeout`
+  if (false !== this._timeout) {
+    var timeout = this._timeout;
+    debug('connect attempt will timeout after %d', timeout);
+
+    // set timer
+    var timer = setTimeout(function(){
+      debug('connect attempt timed out after %d', timeout);
+      openSub.destroy();
+      socket.close();
+      socket.emit('error', 'timeout');
+      self.emitAll('connect_timeout', timeout);
+    }, timeout);
+
+    this.subs.push({
+      destroy: function(){
+        clearTimeout(timer);
+      }
+    });
+  }
+
+  this.subs.push(openSub);
+  this.subs.push(errorSub);
+
+  return this;
+};
+
+/**
+ * Called upon transport open.
+ *
+ * @api private
+ */
+
+Manager.prototype.onopen = function(){
+  debug('open');
+
+  // clear old subs
+  this.cleanup();
+
+  // mark as open
+  this.readyState = 'open';
+  this.emit('open');
+
+  // add new subs
+  var socket = this.engine;
+  this.subs.push(on(socket, 'data', bind(this, 'ondata')));
+  this.subs.push(on(socket, 'ping', bind(this, 'onping')));
+  this.subs.push(on(socket, 'pong', bind(this, 'onpong')));
+  this.subs.push(on(socket, 'error', bind(this, 'onerror')));
+  this.subs.push(on(socket, 'close', bind(this, 'onclose')));
+  this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded')));
+};
+
+/**
+ * Called upon a ping.
+ *
+ * @api private
+ */
+
+Manager.prototype.onping = function(){
+  this.lastPing = new Date;
+  this.emitAll('ping');
+};
+
+/**
+ * Called upon a packet.
+ *
+ * @api private
+ */
+
+Manager.prototype.onpong = function(){
+  this.emitAll('pong', new Date - this.lastPing);
+};
+
+/**
+ * Called with data.
+ *
+ * @api private
+ */
+
+Manager.prototype.ondata = function(data){
+  this.decoder.add(data);
+};
+
+/**
+ * Called when parser fully decodes a packet.
+ *
+ * @api private
+ */
+
+Manager.prototype.ondecoded = function(packet) {
+  this.emit('packet', packet);
+};
+
+/**
+ * Called upon socket error.
+ *
+ * @api private
+ */
+
+Manager.prototype.onerror = function(err){
+  debug('error', err);
+  this.emitAll('error', err);
+};
+
+/**
+ * Creates a new socket for the given `nsp`.
+ *
+ * @return {Socket}
+ * @api public
+ */
+
+Manager.prototype.socket = function(nsp){
+  var socket = this.nsps[nsp];
+  if (!socket) {
+    socket = new Socket(this, nsp);
+    this.nsps[nsp] = socket;
+    var self = this;
+    socket.on('connect', function(){
+      socket.id = self.engine.id;
+      if (!~indexOf(self.connected, socket)) {
+        self.connected.push(socket);
+      }
+    });
+  }
+  return socket;
+};
+
+/**
+ * Called upon a socket close.
+ *
+ * @param {Socket} socket
+ */
+
+Manager.prototype.destroy = function(socket){
+  var index = indexOf(this.connected, socket);
+  if (~index) this.connected.splice(index, 1);
+  if (this.connected.length) return;
+
+  this.close();
+};
+
+/**
+ * Writes a packet.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Manager.prototype.packet = function(packet){
+  debug('writing packet %j', packet);
+  var self = this;
+
+  if (!self.encoding) {
+    // encode, then write to engine with result
+    self.encoding = true;
+    this.encoder.encode(packet, function(encodedPackets) {
+      for (var i = 0; i < encodedPackets.length; i++) {
+        self.engine.write(encodedPackets[i], packet.options);
+      }
+      self.encoding = false;
+      self.processPacketQueue();
+    });
+  } else { // add packet to the queue
+    self.packetBuffer.push(packet);
+  }
+};
+
+/**
+ * If packet buffer is non-empty, begins encoding the
+ * next packet in line.
+ *
+ * @api private
+ */
+
+Manager.prototype.processPacketQueue = function() {
+  if (this.packetBuffer.length > 0 && !this.encoding) {
+    var pack = this.packetBuffer.shift();
+    this.packet(pack);
+  }
+};
+
+/**
+ * Clean up transport subscriptions and packet buffer.
+ *
+ * @api private
+ */
+
+Manager.prototype.cleanup = function(){
+  debug('cleanup');
+
+  var sub;
+  while (sub = this.subs.shift()) sub.destroy();
+
+  this.packetBuffer = [];
+  this.encoding = false;
+  this.lastPing = null;
+
+  this.decoder.destroy();
+};
+
+/**
+ * Close the current socket.
+ *
+ * @api private
+ */
+
+Manager.prototype.close =
+Manager.prototype.disconnect = function(){
+  debug('disconnect');
+  this.skipReconnect = true;
+  this.reconnecting = false;
+  if ('opening' == this.readyState) {
+    // `onclose` will not fire because
+    // an open event never happened
+    this.cleanup();
+  }
+  this.backoff.reset();
+  this.readyState = 'closed';
+  if (this.engine) this.engine.close();
+};
+
+/**
+ * Called upon engine close.
+ *
+ * @api private
+ */
+
+Manager.prototype.onclose = function(reason){
+  debug('onclose');
+
+  this.cleanup();
+  this.backoff.reset();
+  this.readyState = 'closed';
+  this.emit('close', reason);
+
+  if (this._reconnection && !this.skipReconnect) {
+    this.reconnect();
+  }
+};
+
+/**
+ * Attempt a reconnection.
+ *
+ * @api private
+ */
+
+Manager.prototype.reconnect = function(){
+  if (this.reconnecting || this.skipReconnect) return this;
+
+  var self = this;
+
+  if (this.backoff.attempts >= this._reconnectionAttempts) {
+    debug('reconnect failed');
+    this.backoff.reset();
+    this.emitAll('reconnect_failed');
+    this.reconnecting = false;
+  } else {
+    var delay = this.backoff.duration();
+    debug('will wait %dms before reconnect attempt', delay);
+
+    this.reconnecting = true;
+    var timer = setTimeout(function(){
+      if (self.skipReconnect) return;
+
+      debug('attempting reconnect');
+      self.emitAll('reconnect_attempt', self.backoff.attempts);
+      self.emitAll('reconnecting', self.backoff.attempts);
+
+      // check again for the case socket closed in above events
+      if (self.skipReconnect) return;
+
+      self.open(function(err){
+        if (err) {
+          debug('reconnect attempt error');
+          self.reconnecting = false;
+          self.reconnect();
+          self.emitAll('reconnect_error', err.data);
+        } else {
+          debug('reconnect success');
+          self.onreconnect();
+        }
+      });
+    }, delay);
+
+    this.subs.push({
+      destroy: function(){
+        clearTimeout(timer);
+      }
+    });
+  }
+};
+
+/**
+ * Called upon successful reconnect.
+ *
+ * @api private
+ */
+
+Manager.prototype.onreconnect = function(){
+  var attempt = this.backoff.attempts;
+  this.reconnecting = false;
+  this.backoff.reset();
+  this.updateSocketIds();
+  this.emitAll('reconnect', attempt);
+};
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/on.js b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/on.js
new file mode 100644
index 0000000..6be286d
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/on.js
@@ -0,0 +1,24 @@
+
+/**
+ * Module exports.
+ */
+
+module.exports = on;
+
+/**
+ * Helper for subscriptions.
+ *
+ * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter`
+ * @param {String} event name
+ * @param {Function} callback
+ * @api public
+ */
+
+function on(obj, ev, fn) {
+  obj.on(ev, fn);
+  return {
+    destroy: function(){
+      obj.removeListener(ev, fn);
+    }
+  };
+}
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/socket.js b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/socket.js
new file mode 100644
index 0000000..cf7d6a1
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/socket.js
@@ -0,0 +1,411 @@
+
+/**
+ * Module dependencies.
+ */
+
+var parser = require('socket.io-parser');
+var Emitter = require('component-emitter');
+var toArray = require('to-array');
+var on = require('./on');
+var bind = require('component-bind');
+var debug = require('debug')('socket.io-client:socket');
+var hasBin = require('has-binary');
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = Socket;
+
+/**
+ * Internal events (blacklisted).
+ * These events can't be emitted by the user.
+ *
+ * @api private
+ */
+
+var events = {
+  connect: 1,
+  connect_error: 1,
+  connect_timeout: 1,
+  disconnect: 1,
+  error: 1,
+  reconnect: 1,
+  reconnect_attempt: 1,
+  reconnect_failed: 1,
+  reconnect_error: 1,
+  reconnecting: 1,
+  ping: 1,
+  pong: 1
+};
+
+/**
+ * Shortcut to `Emitter#emit`.
+ */
+
+var emit = Emitter.prototype.emit;
+
+/**
+ * `Socket` constructor.
+ *
+ * @api public
+ */
+
+function Socket(io, nsp){
+  this.io = io;
+  this.nsp = nsp;
+  this.json = this; // compat
+  this.ids = 0;
+  this.acks = {};
+  if (this.io.autoConnect) this.open();
+  this.receiveBuffer = [];
+  this.sendBuffer = [];
+  this.connected = false;
+  this.disconnected = true;
+}
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Socket.prototype);
+
+/**
+ * Subscribe to open, close and packet events
+ *
+ * @api private
+ */
+
+Socket.prototype.subEvents = function() {
+  if (this.subs) return;
+
+  var io = this.io;
+  this.subs = [
+    on(io, 'open', bind(this, 'onopen')),
+    on(io, 'packet', bind(this, 'onpacket')),
+    on(io, 'close', bind(this, 'onclose'))
+  ];
+};
+
+/**
+ * "Opens" the socket.
+ *
+ * @api public
+ */
+
+Socket.prototype.open =
+Socket.prototype.connect = function(){
+  if (this.connected) return this;
+
+  this.subEvents();
+  this.io.open(); // ensure open
+  if ('open' == this.io.readyState) this.onopen();
+  return this;
+};
+
+/**
+ * Sends a `message` event.
+ *
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.send = function(){
+  var args = toArray(arguments);
+  args.unshift('message');
+  this.emit.apply(this, args);
+  return this;
+};
+
+/**
+ * Override `emit`.
+ * If the event is in `events`, it's emitted normally.
+ *
+ * @param {String} event name
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.emit = function(ev){
+  if (events.hasOwnProperty(ev)) {
+    emit.apply(this, arguments);
+    return this;
+  }
+
+  var args = toArray(arguments);
+  var parserType = parser.EVENT; // default
+  if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary
+  var packet = { type: parserType, data: args };
+
+  packet.options = {};
+  packet.options.compress = !this.flags || false !== this.flags.compress;
+
+  // event ack callback
+  if ('function' == typeof args[args.length - 1]) {
+    debug('emitting packet with ack id %d', this.ids);
+    this.acks[this.ids] = args.pop();
+    packet.id = this.ids++;
+  }
+
+  if (this.connected) {
+    this.packet(packet);
+  } else {
+    this.sendBuffer.push(packet);
+  }
+
+  delete this.flags;
+
+  return this;
+};
+
+/**
+ * Sends a packet.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.packet = function(packet){
+  packet.nsp = this.nsp;
+  this.io.packet(packet);
+};
+
+/**
+ * Called upon engine `open`.
+ *
+ * @api private
+ */
+
+Socket.prototype.onopen = function(){
+  debug('transport is open - connecting');
+
+  // write connect packet if necessary
+  if ('/' != this.nsp) {
+    this.packet({ type: parser.CONNECT, options: { compress: true } });
+  }
+};
+
+/**
+ * Called upon engine `close`.
+ *
+ * @param {String} reason
+ * @api private
+ */
+
+Socket.prototype.onclose = function(reason){
+  debug('close (%s)', reason);
+  this.connected = false;
+  this.disconnected = true;
+  delete this.id;
+  this.emit('disconnect', reason);
+};
+
+/**
+ * Called with socket packet.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.onpacket = function(packet){
+  if (packet.nsp != this.nsp) return;
+
+  switch (packet.type) {
+    case parser.CONNECT:
+      this.onconnect();
+      break;
+
+    case parser.EVENT:
+      this.onevent(packet);
+      break;
+
+    case parser.BINARY_EVENT:
+      this.onevent(packet);
+      break;
+
+    case parser.ACK:
+      this.onack(packet);
+      break;
+
+    case parser.BINARY_ACK:
+      this.onack(packet);
+      break;
+
+    case parser.DISCONNECT:
+      this.ondisconnect();
+      break;
+
+    case parser.ERROR:
+      this.emit('error', packet.data);
+      break;
+  }
+};
+
+/**
+ * Called upon a server event.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.onevent = function(packet){
+  var args = packet.data || [];
+  debug('emitting event %j', args);
+
+  if (null != packet.id) {
+    debug('attaching ack callback to event');
+    args.push(this.ack(packet.id));
+  }
+
+  if (this.connected) {
+    emit.apply(this, args);
+  } else {
+    this.receiveBuffer.push(args);
+  }
+};
+
+/**
+ * Produces an ack callback to emit with an event.
+ *
+ * @api private
+ */
+
+Socket.prototype.ack = function(id){
+  var self = this;
+  var sent = false;
+  return function(){
+    // prevent double callbacks
+    if (sent) return;
+    sent = true;
+    var args = toArray(arguments);
+    debug('sending ack %j', args);
+
+    var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;
+    self.packet({
+      type: type,
+      id: id,
+      data: args,
+      options: { compress: true }
+    });
+  };
+};
+
+/**
+ * Called upon a server acknowlegement.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.onack = function(packet){
+  var ack = this.acks[packet.id];
+  if ('function' == typeof ack) {
+    debug('calling ack %s with %j', packet.id, packet.data);
+    ack.apply(this, packet.data);
+    delete this.acks[packet.id];
+  } else {
+    debug('bad ack %s', packet.id);
+  }
+};
+
+/**
+ * Called upon server connect.
+ *
+ * @api private
+ */
+
+Socket.prototype.onconnect = function(){
+  this.connected = true;
+  this.disconnected = false;
+  this.emit('connect');
+  this.emitBuffered();
+};
+
+/**
+ * Emit buffered events (received and emitted).
+ *
+ * @api private
+ */
+
+Socket.prototype.emitBuffered = function(){
+  var i;
+  for (i = 0; i < this.receiveBuffer.length; i++) {
+    emit.apply(this, this.receiveBuffer[i]);
+  }
+  this.receiveBuffer = [];
+
+  for (i = 0; i < this.sendBuffer.length; i++) {
+    this.packet(this.sendBuffer[i]);
+  }
+  this.sendBuffer = [];
+};
+
+/**
+ * Called upon server disconnect.
+ *
+ * @api private
+ */
+
+Socket.prototype.ondisconnect = function(){
+  debug('server disconnect (%s)', this.nsp);
+  this.destroy();
+  this.onclose('io server disconnect');
+};
+
+/**
+ * Called upon forced client/server side disconnections,
+ * this method ensures the manager stops tracking us and
+ * that reconnections don't get triggered for this.
+ *
+ * @api private.
+ */
+
+Socket.prototype.destroy = function(){
+  if (this.subs) {
+    // clean subscriptions to avoid reconnections
+    for (var i = 0; i < this.subs.length; i++) {
+      this.subs[i].destroy();
+    }
+    this.subs = null;
+  }
+
+  this.io.destroy(this);
+};
+
+/**
+ * Disconnects the socket manually.
+ *
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.close =
+Socket.prototype.disconnect = function(){
+  if (this.connected) {
+    debug('performing disconnect (%s)', this.nsp);
+    this.packet({ type: parser.DISCONNECT, options: { compress: true } });
+  }
+
+  // remove socket from pool
+  this.destroy();
+
+  if (this.connected) {
+    // fire events
+    this.onclose('io client disconnect');
+  }
+  return this;
+};
+
+/**
+ * Sets the compress flag.
+ *
+ * @param {Boolean} if `true`, compresses the sending data
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.compress = function(compress){
+  this.flags = this.flags || {};
+  this.flags.compress = compress;
+  return this;
+};
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/url.js b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/url.js
new file mode 100644
index 0000000..6000b6f
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/url.js
@@ -0,0 +1,73 @@
+
+/**
+ * Module dependencies.
+ */
+
+var parseuri = require('parseuri');
+var debug = require('debug')('socket.io-client:url');
+
+/**
+ * Module exports.
+ */
+
+module.exports = url;
+
+/**
+ * URL parser.
+ *
+ * @param {String} url
+ * @param {Object} An object meant to mimic window.location.
+ *                 Defaults to window.location.
+ * @api public
+ */
+
+function url(uri, loc){
+  var obj = uri;
+
+  // default to window.location
+  var loc = loc || global.location;
+  if (null == uri) uri = loc.protocol + '//' + loc.host;
+
+  // relative path support
+  if ('string' == typeof uri) {
+    if ('/' == uri.charAt(0)) {
+      if ('/' == uri.charAt(1)) {
+        uri = loc.protocol + uri;
+      } else {
+        uri = loc.host + uri;
+      }
+    }
+
+    if (!/^(https?|wss?):\/\//.test(uri)) {
+      debug('protocol-less url %s', uri);
+      if ('undefined' != typeof loc) {
+        uri = loc.protocol + '//' + uri;
+      } else {
+        uri = 'https://' + uri;
+      }
+    }
+
+    // parse
+    debug('parse %s', uri);
+    obj = parseuri(uri);
+  }
+
+  // make sure we treat `localhost:80` and `localhost` equally
+  if (!obj.port) {
+    if (/^(http|ws)$/.test(obj.protocol)) {
+      obj.port = '80';
+    }
+    else if (/^(http|ws)s$/.test(obj.protocol)) {
+      obj.port = '443';
+    }
+  }
+
+  obj.path = obj.path || '/';
+
+  // define unique id
+  obj.id = obj.protocol + '://' + obj.host + ':' + obj.port;
+  // define href
+  obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port));
+
+  return obj;
+}
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/socket.io.js b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/socket.io.js
new file mode 100644
index 0000000..fd3c75b
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/socket.io.js
@@ -0,0 +1,7006 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.io=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+
+module.exports = _dereq_('./lib/');
+
+},{"./lib/":2}],2:[function(_dereq_,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var url = _dereq_('./url');
+var parser = _dereq_('socket.io-parser');
+var Manager = _dereq_('./manager');
+var debug = _dereq_('debug')('socket.io-client');
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = lookup;
+
+/**
+ * Managers cache.
+ */
+
+var cache = exports.managers = {};
+
+/**
+ * Looks up an existing `Manager` for multiplexing.
+ * If the user summons:
+ *
+ *   `io('http://localhost/a');`
+ *   `io('http://localhost/b');`
+ *
+ * We reuse the existing instance based on same scheme/port/host,
+ * and we initialize sockets for each namespace.
+ *
+ * @api public
+ */
+
+function lookup(uri, opts) {
+  if (typeof uri == 'object') {
+    opts = uri;
+    uri = undefined;
+  }
+
+  opts = opts || {};
+
+  var parsed = url(uri);
+  var source = parsed.source;
+  var id = parsed.id;
+  var path = parsed.path;
+  var sameNamespace = (cache[id] && cache[id].nsps[path] &&
+                       path == cache[id].nsps[path].nsp);
+  var newConnection = opts.forceNew || opts['force new connection'] ||
+                      false === opts.multiplex || sameNamespace;
+
+  var io;
+
+  if (newConnection) {
+    debug('ignoring socket cache for %s', source);
+    io = Manager(source, opts);
+  } else {
+    if (!cache[id]) {
+      debug('new io instance for %s', source);
+      cache[id] = Manager(source, opts);
+    }
+    io = cache[id];
+  }
+
+  return io.socket(parsed.path);
+}
+
+/**
+ * Protocol version.
+ *
+ * @api public
+ */
+
+exports.protocol = parser.protocol;
+
+/**
+ * `connect`.
+ *
+ * @param {String} uri
+ * @api public
+ */
+
+exports.connect = lookup;
+
+/**
+ * Expose constructors for standalone build.
+ *
+ * @api public
+ */
+
+exports.Manager = _dereq_('./manager');
+exports.Socket = _dereq_('./socket');
+
+},{"./manager":3,"./socket":5,"./url":6,"debug":10,"socket.io-parser":46}],3:[function(_dereq_,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var url = _dereq_('./url');
+var eio = _dereq_('engine.io-client');
+var Socket = _dereq_('./socket');
+var Emitter = _dereq_('component-emitter');
+var parser = _dereq_('socket.io-parser');
+var on = _dereq_('./on');
+var bind = _dereq_('component-bind');
+var object = _dereq_('object-component');
+var debug = _dereq_('debug')('socket.io-client:manager');
+var indexOf = _dereq_('indexof');
+var Backoff = _dereq_('backo2');
+
+/**
+ * Module exports
+ */
+
+module.exports = Manager;
+
+/**
+ * `Manager` constructor.
+ *
+ * @param {String} engine instance or engine uri/opts
+ * @param {Object} options
+ * @api public
+ */
+
+function Manager(uri, opts){
+  if (!(this instanceof Manager)) return new Manager(uri, opts);
+  if (uri && ('object' == typeof uri)) {
+    opts = uri;
+    uri = undefined;
+  }
+  opts = opts || {};
+
+  opts.path = opts.path || '/socket.io';
+  this.nsps = {};
+  this.subs = [];
+  this.opts = opts;
+  this.reconnection(opts.reconnection !== false);
+  this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
+  this.reconnectionDelay(opts.reconnectionDelay || 1000);
+  this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
+  this.randomizationFactor(opts.randomizationFactor || 0.5);
+  this.backoff = new Backoff({
+    min: this.reconnectionDelay(),
+    max: this.reconnectionDelayMax(),
+    jitter: this.randomizationFactor()
+  });
+  this.timeout(null == opts.timeout ? 20000 : opts.timeout);
+  this.readyState = 'closed';
+  this.uri = uri;
+  this.connected = [];
+  this.encoding = false;
+  this.packetBuffer = [];
+  this.encoder = new parser.Encoder();
+  this.decoder = new parser.Decoder();
+  this.autoConnect = opts.autoConnect !== false;
+  if (this.autoConnect) this.open();
+}
+
+/**
+ * Propagate given event to sockets and emit on `this`
+ *
+ * @api private
+ */
+
+Manager.prototype.emitAll = function() {
+  this.emit.apply(this, arguments);
+  for (var nsp in this.nsps) {
+    this.nsps[nsp].emit.apply(this.nsps[nsp], arguments);
+  }
+};
+
+/**
+ * Update `socket.id` of all sockets
+ *
+ * @api private
+ */
+
+Manager.prototype.updateSocketIds = function(){
+  for (var nsp in this.nsps) {
+    this.nsps[nsp].id = this.engine.id;
+  }
+};
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Manager.prototype);
+
+/**
+ * Sets the `reconnection` config.
+ *
+ * @param {Boolean} true/false if it should automatically reconnect
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnection = function(v){
+  if (!arguments.length) return this._reconnection;
+  this._reconnection = !!v;
+  return this;
+};
+
+/**
+ * Sets the reconnection attempts config.
+ *
+ * @param {Number} max reconnection attempts before giving up
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnectionAttempts = function(v){
+  if (!arguments.length) return this._reconnectionAttempts;
+  this._reconnectionAttempts = v;
+  return this;
+};
+
+/**
+ * Sets the delay between reconnections.
+ *
+ * @param {Number} delay
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnectionDelay = function(v){
+  if (!arguments.length) return this._reconnectionDelay;
+  this._reconnectionDelay = v;
+  this.backoff && this.backoff.setMin(v);
+  return this;
+};
+
+Manager.prototype.randomizationFactor = function(v){
+  if (!arguments.length) return this._randomizationFactor;
+  this._randomizationFactor = v;
+  this.backoff && this.backoff.setJitter(v);
+  return this;
+};
+
+/**
+ * Sets the maximum delay between reconnections.
+ *
+ * @param {Number} delay
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.reconnectionDelayMax = function(v){
+  if (!arguments.length) return this._reconnectionDelayMax;
+  this._reconnectionDelayMax = v;
+  this.backoff && this.backoff.setMax(v);
+  return this;
+};
+
+/**
+ * Sets the connection timeout. `false` to disable
+ *
+ * @return {Manager} self or value
+ * @api public
+ */
+
+Manager.prototype.timeout = function(v){
+  if (!arguments.length) return this._timeout;
+  this._timeout = v;
+  return this;
+};
+
+/**
+ * Starts trying to reconnect if reconnection is enabled and we have not
+ * started reconnecting yet
+ *
+ * @api private
+ */
+
+Manager.prototype.maybeReconnectOnOpen = function() {
+  // Only try to reconnect if it's the first time we're connecting
+  if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) {
+    // keeps reconnection from firing twice for the same reconnection loop
+    this.reconnect();
+  }
+};
+
+
+/**
+ * Sets the current transport `socket`.
+ *
+ * @param {Function} optional, callback
+ * @return {Manager} self
+ * @api public
+ */
+
+Manager.prototype.open =
+Manager.prototype.connect = function(fn){
+  debug('readyState %s', this.readyState);
+  if (~this.readyState.indexOf('open')) return this;
+
+  debug('opening %s', this.uri);
+  this.engine = eio(this.uri, this.opts);
+  var socket = this.engine;
+  var self = this;
+  this.readyState = 'opening';
+  this.skipReconnect = false;
+
+  // emit `open`
+  var openSub = on(socket, 'open', function() {
+    self.onopen();
+    fn && fn();
+  });
+
+  // emit `connect_error`
+  var errorSub = on(socket, 'error', function(data){
+    debug('connect_error');
+    self.cleanup();
+    self.readyState = 'closed';
+    self.emitAll('connect_error', data);
+    if (fn) {
+      var err = new Error('Connection error');
+      err.data = data;
+      fn(err);
+    } else {
+      // Only do this if there is no fn to handle the error
+      self.maybeReconnectOnOpen();
+    }
+  });
+
+  // emit `connect_timeout`
+  if (false !== this._timeout) {
+    var timeout = this._timeout;
+    debug('connect attempt will timeout after %d', timeout);
+
+    // set timer
+    var timer = setTimeout(function(){
+      debug('connect attempt timed out after %d', timeout);
+      openSub.destroy();
+      socket.close();
+      socket.emit('error', 'timeout');
+      self.emitAll('connect_timeout', timeout);
+    }, timeout);
+
+    this.subs.push({
+      destroy: function(){
+        clearTimeout(timer);
+      }
+    });
+  }
+
+  this.subs.push(openSub);
+  this.subs.push(errorSub);
+
+  return this;
+};
+
+/**
+ * Called upon transport open.
+ *
+ * @api private
+ */
+
+Manager.prototype.onopen = function(){
+  debug('open');
+
+  // clear old subs
+  this.cleanup();
+
+  // mark as open
+  this.readyState = 'open';
+  this.emit('open');
+
+  // add new subs
+  var socket = this.engine;
+  this.subs.push(on(socket, 'data', bind(this, 'ondata')));
+  this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded')));
+  this.subs.push(on(socket, 'error', bind(this, 'onerror')));
+  this.subs.push(on(socket, 'close', bind(this, 'onclose')));
+};
+
+/**
+ * Called with data.
+ *
+ * @api private
+ */
+
+Manager.prototype.ondata = function(data){
+  this.decoder.add(data);
+};
+
+/**
+ * Called when parser fully decodes a packet.
+ *
+ * @api private
+ */
+
+Manager.prototype.ondecoded = function(packet) {
+  this.emit('packet', packet);
+};
+
+/**
+ * Called upon socket error.
+ *
+ * @api private
+ */
+
+Manager.prototype.onerror = function(err){
+  debug('error', err);
+  this.emitAll('error', err);
+};
+
+/**
+ * Creates a new socket for the given `nsp`.
+ *
+ * @return {Socket}
+ * @api public
+ */
+
+Manager.prototype.socket = function(nsp){
+  var socket = this.nsps[nsp];
+  if (!socket) {
+    socket = new Socket(this, nsp);
+    this.nsps[nsp] = socket;
+    var self = this;
+    socket.on('connect', function(){
+      socket.id = self.engine.id;
+      if (!~indexOf(self.connected, socket)) {
+        self.connected.push(socket);
+      }
+    });
+  }
+  return socket;
+};
+
+/**
+ * Called upon a socket close.
+ *
+ * @param {Socket} socket
+ */
+
+Manager.prototype.destroy = function(socket){
+  var index = indexOf(this.connected, socket);
+  if (~index) this.connected.splice(index, 1);
+  if (this.connected.length) return;
+
+  this.close();
+};
+
+/**
+ * Writes a packet.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Manager.prototype.packet = function(packet){
+  debug('writing packet %j', packet);
+  var self = this;
+
+  if (!self.encoding) {
+    // encode, then write to engine with result
+    self.encoding = true;
+    this.encoder.encode(packet, function(encodedPackets) {
+      for (var i = 0; i < encodedPackets.length; i++) {
+        self.engine.write(encodedPackets[i]);
+      }
+      self.encoding = false;
+      self.processPacketQueue();
+    });
+  } else { // add packet to the queue
+    self.packetBuffer.push(packet);
+  }
+};
+
+/**
+ * If packet buffer is non-empty, begins encoding the
+ * next packet in line.
+ *
+ * @api private
+ */
+
+Manager.prototype.processPacketQueue = function() {
+  if (this.packetBuffer.length > 0 && !this.encoding) {
+    var pack = this.packetBuffer.shift();
+    this.packet(pack);
+  }
+};
+
+/**
+ * Clean up transport subscriptions and packet buffer.
+ *
+ * @api private
+ */
+
+Manager.prototype.cleanup = function(){
+  var sub;
+  while (sub = this.subs.shift()) sub.destroy();
+
+  this.packetBuffer = [];
+  this.encoding = false;
+
+  this.decoder.destroy();
+};
+
+/**
+ * Close the current socket.
+ *
+ * @api private
+ */
+
+Manager.prototype.close =
+Manager.prototype.disconnect = function(){
+  this.skipReconnect = true;
+  this.backoff.reset();
+  this.readyState = 'closed';
+  this.engine && this.engine.close();
+};
+
+/**
+ * Called upon engine close.
+ *
+ * @api private
+ */
+
+Manager.prototype.onclose = function(reason){
+  debug('close');
+  this.cleanup();
+  this.backoff.reset();
+  this.readyState = 'closed';
+  this.emit('close', reason);
+  if (this._reconnection && !this.skipReconnect) {
+    this.reconnect();
+  }
+};
+
+/**
+ * Attempt a reconnection.
+ *
+ * @api private
+ */
+
+Manager.prototype.reconnect = function(){
+  if (this.reconnecting || this.skipReconnect) return this;
+
+  var self = this;
+
+  if (this.backoff.attempts >= this._reconnectionAttempts) {
+    debug('reconnect failed');
+    this.backoff.reset();
+    this.emitAll('reconnect_failed');
+    this.reconnecting = false;
+  } else {
+    var delay = this.backoff.duration();
+    debug('will wait %dms before reconnect attempt', delay);
+
+    this.reconnecting = true;
+    var timer = setTimeout(function(){
+      if (self.skipReconnect) return;
+
+      debug('attempting reconnect');
+      self.emitAll('reconnect_attempt', self.backoff.attempts);
+      self.emitAll('reconnecting', self.backoff.attempts);
+
+      // check again for the case socket closed in above events
+      if (self.skipReconnect) return;
+
+      self.open(function(err){
+        if (err) {
+          debug('reconnect attempt error');
+          self.reconnecting = false;
+          self.reconnect();
+          self.emitAll('reconnect_error', err.data);
+        } else {
+          debug('reconnect success');
+          self.onreconnect();
+        }
+      });
+    }, delay);
+
+    this.subs.push({
+      destroy: function(){
+        clearTimeout(timer);
+      }
+    });
+  }
+};
+
+/**
+ * Called upon successful reconnect.
+ *
+ * @api private
+ */
+
+Manager.prototype.onreconnect = function(){
+  var attempt = this.backoff.attempts;
+  this.reconnecting = false;
+  this.backoff.reset();
+  this.updateSocketIds();
+  this.emitAll('reconnect', attempt);
+};
+
+},{"./on":4,"./socket":5,"./url":6,"backo2":7,"component-bind":8,"component-emitter":9,"debug":10,"engine.io-client":11,"indexof":42,"object-component":43,"socket.io-parser":46}],4:[function(_dereq_,module,exports){
+
+/**
+ * Module exports.
+ */
+
+module.exports = on;
+
+/**
+ * Helper for subscriptions.
+ *
+ * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter`
+ * @param {String} event name
+ * @param {Function} callback
+ * @api public
+ */
+
+function on(obj, ev, fn) {
+  obj.on(ev, fn);
+  return {
+    destroy: function(){
+      obj.removeListener(ev, fn);
+    }
+  };
+}
+
+},{}],5:[function(_dereq_,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var parser = _dereq_('socket.io-parser');
+var Emitter = _dereq_('component-emitter');
+var toArray = _dereq_('to-array');
+var on = _dereq_('./on');
+var bind = _dereq_('component-bind');
+var debug = _dereq_('debug')('socket.io-client:socket');
+var hasBin = _dereq_('has-binary');
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = Socket;
+
+/**
+ * Internal events (blacklisted).
+ * These events can't be emitted by the user.
+ *
+ * @api private
+ */
+
+var events = {
+  connect: 1,
+  connect_error: 1,
+  connect_timeout: 1,
+  disconnect: 1,
+  error: 1,
+  reconnect: 1,
+  reconnect_attempt: 1,
+  reconnect_failed: 1,
+  reconnect_error: 1,
+  reconnecting: 1
+};
+
+/**
+ * Shortcut to `Emitter#emit`.
+ */
+
+var emit = Emitter.prototype.emit;
+
+/**
+ * `Socket` constructor.
+ *
+ * @api public
+ */
+
+function Socket(io, nsp){
+  this.io = io;
+  this.nsp = nsp;
+  this.json = this; // compat
+  this.ids = 0;
+  this.acks = {};
+  if (this.io.autoConnect) this.open();
+  this.receiveBuffer = [];
+  this.sendBuffer = [];
+  this.connected = false;
+  this.disconnected = true;
+}
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Socket.prototype);
+
+/**
+ * Subscribe to open, close and packet events
+ *
+ * @api private
+ */
+
+Socket.prototype.subEvents = function() {
+  if (this.subs) return;
+
+  var io = this.io;
+  this.subs = [
+    on(io, 'open', bind(this, 'onopen')),
+    on(io, 'packet', bind(this, 'onpacket')),
+    on(io, 'close', bind(this, 'onclose'))
+  ];
+};
+
+/**
+ * "Opens" the socket.
+ *
+ * @api public
+ */
+
+Socket.prototype.open =
+Socket.prototype.connect = function(){
+  if (this.connected) return this;
+
+  this.subEvents();
+  this.io.open(); // ensure open
+  if ('open' == this.io.readyState) this.onopen();
+  return this;
+};
+
+/**
+ * Sends a `message` event.
+ *
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.send = function(){
+  var args = toArray(arguments);
+  args.unshift('message');
+  this.emit.apply(this, args);
+  return this;
+};
+
+/**
+ * Override `emit`.
+ * If the event is in `events`, it's emitted normally.
+ *
+ * @param {String} event name
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.emit = function(ev){
+  if (events.hasOwnProperty(ev)) {
+    emit.apply(this, arguments);
+    return this;
+  }
+
+  var args = toArray(arguments);
+  var parserType = parser.EVENT; // default
+  if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary
+  var packet = { type: parserType, data: args };
+
+  // event ack callback
+  if ('function' == typeof args[args.length - 1]) {
+    debug('emitting packet with ack id %d', this.ids);
+    this.acks[this.ids] = args.pop();
+    packet.id = this.ids++;
+  }
+
+  if (this.connected) {
+    this.packet(packet);
+  } else {
+    this.sendBuffer.push(packet);
+  }
+
+  return this;
+};
+
+/**
+ * Sends a packet.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.packet = function(packet){
+  packet.nsp = this.nsp;
+  this.io.packet(packet);
+};
+
+/**
+ * Called upon engine `open`.
+ *
+ * @api private
+ */
+
+Socket.prototype.onopen = function(){
+  debug('transport is open - connecting');
+
+  // write connect packet if necessary
+  if ('/' != this.nsp) {
+    this.packet({ type: parser.CONNECT });
+  }
+};
+
+/**
+ * Called upon engine `close`.
+ *
+ * @param {String} reason
+ * @api private
+ */
+
+Socket.prototype.onclose = function(reason){
+  debug('close (%s)', reason);
+  this.connected = false;
+  this.disconnected = true;
+  delete this.id;
+  this.emit('disconnect', reason);
+};
+
+/**
+ * Called with socket packet.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.onpacket = function(packet){
+  if (packet.nsp != this.nsp) return;
+
+  switch (packet.type) {
+    case parser.CONNECT:
+      this.onconnect();
+      break;
+
+    case parser.EVENT:
+      this.onevent(packet);
+      break;
+
+    case parser.BINARY_EVENT:
+      this.onevent(packet);
+      break;
+
+    case parser.ACK:
+      this.onack(packet);
+      break;
+
+    case parser.BINARY_ACK:
+      this.onack(packet);
+      break;
+
+    case parser.DISCONNECT:
+      this.ondisconnect();
+      break;
+
+    case parser.ERROR:
+      this.emit('error', packet.data);
+      break;
+  }
+};
+
+/**
+ * Called upon a server event.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.onevent = function(packet){
+  var args = packet.data || [];
+  debug('emitting event %j', args);
+
+  if (null != packet.id) {
+    debug('attaching ack callback to event');
+    args.push(this.ack(packet.id));
+  }
+
+  if (this.connected) {
+    emit.apply(this, args);
+  } else {
+    this.receiveBuffer.push(args);
+  }
+};
+
+/**
+ * Produces an ack callback to emit with an event.
+ *
+ * @api private
+ */
+
+Socket.prototype.ack = function(id){
+  var self = this;
+  var sent = false;
+  return function(){
+    // prevent double callbacks
+    if (sent) return;
+    sent = true;
+    var args = toArray(arguments);
+    debug('sending ack %j', args);
+
+    var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;
+    self.packet({
+      type: type,
+      id: id,
+      data: args
+    });
+  };
+};
+
+/**
+ * Called upon a server acknowlegement.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.onack = function(packet){
+  debug('calling ack %s with %j', packet.id, packet.data);
+  var fn = this.acks[packet.id];
+  fn.apply(this, packet.data);
+  delete this.acks[packet.id];
+};
+
+/**
+ * Called upon server connect.
+ *
+ * @api private
+ */
+
+Socket.prototype.onconnect = function(){
+  this.connected = true;
+  this.disconnected = false;
+  this.emit('connect');
+  this.emitBuffered();
+};
+
+/**
+ * Emit buffered events (received and emitted).
+ *
+ * @api private
+ */
+
+Socket.prototype.emitBuffered = function(){
+  var i;
+  for (i = 0; i < this.receiveBuffer.length; i++) {
+    emit.apply(this, this.receiveBuffer[i]);
+  }
+  this.receiveBuffer = [];
+
+  for (i = 0; i < this.sendBuffer.length; i++) {
+    this.packet(this.sendBuffer[i]);
+  }
+  this.sendBuffer = [];
+};
+
+/**
+ * Called upon server disconnect.
+ *
+ * @api private
+ */
+
+Socket.prototype.ondisconnect = function(){
+  debug('server disconnect (%s)', this.nsp);
+  this.destroy();
+  this.onclose('io server disconnect');
+};
+
+/**
+ * Called upon forced client/server side disconnections,
+ * this method ensures the manager stops tracking us and
+ * that reconnections don't get triggered for this.
+ *
+ * @api private.
+ */
+
+Socket.prototype.destroy = function(){
+  if (this.subs) {
+    // clean subscriptions to avoid reconnections
+    for (var i = 0; i < this.subs.length; i++) {
+      this.subs[i].destroy();
+    }
+    this.subs = null;
+  }
+
+  this.io.destroy(this);
+};
+
+/**
+ * Disconnects the socket manually.
+ *
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.close =
+Socket.prototype.disconnect = function(){
+  if (this.connected) {
+    debug('performing disconnect (%s)', this.nsp);
+    this.packet({ type: parser.DISCONNECT });
+  }
+
+  // remove socket from pool
+  this.destroy();
+
+  if (this.connected) {
+    // fire events
+    this.onclose('io client disconnect');
+  }
+  return this;
+};
+
+},{"./on":4,"component-bind":8,"component-emitter":9,"debug":10,"has-binary":38,"socket.io-parser":46,"to-array":50}],6:[function(_dereq_,module,exports){
+(function (global){
+
+/**
+ * Module dependencies.
+ */
+
+var parseuri = _dereq_('parseuri');
+var debug = _dereq_('debug')('socket.io-client:url');
+
+/**
+ * Module exports.
+ */
+
+module.exports = url;
+
+/**
+ * URL parser.
+ *
+ * @param {String} url
+ * @param {Object} An object meant to mimic window.location.
+ *                 Defaults to window.location.
+ * @api public
+ */
+
+function url(uri, loc){
+  var obj = uri;
+
+  // default to window.location
+  var loc = loc || global.location;
+  if (null == uri) uri = loc.protocol + '//' + loc.host;
+
+  // relative path support
+  if ('string' == typeof uri) {
+    if ('/' == uri.charAt(0)) {
+      if ('/' == uri.charAt(1)) {
+        uri = loc.protocol + uri;
+      } else {
+        uri = loc.hostname + uri;
+      }
+    }
+
+    if (!/^(https?|wss?):\/\//.test(uri)) {
+      debug('protocol-less url %s', uri);
+      if ('undefined' != typeof loc) {
+        uri = loc.protocol + '//' + uri;
+      } else {
+        uri = 'https://' + uri;
+      }
+    }
+
+    // parse
+    debug('parse %s', uri);
+    obj = parseuri(uri);
+  }
+
+  // make sure we treat `localhost:80` and `localhost` equally
+  if (!obj.port) {
+    if (/^(http|ws)$/.test(obj.protocol)) {
+      obj.port = '80';
+    }
+    else if (/^(http|ws)s$/.test(obj.protocol)) {
+      obj.port = '443';
+    }
+  }
+
+  obj.path = obj.path || '/';
+
+  // define unique id
+  obj.id = obj.protocol + '://' + obj.host + ':' + obj.port;
+  // define href
+  obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port));
+
+  return obj;
+}
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"debug":10,"parseuri":44}],7:[function(_dereq_,module,exports){
+
+/**
+ * Expose `Backoff`.
+ */
+
+module.exports = Backoff;
+
+/**
+ * Initialize backoff timer with `opts`.
+ *
+ * - `min` initial timeout in milliseconds [100]
+ * - `max` max timeout [10000]
+ * - `jitter` [0]
+ * - `factor` [2]
+ *
+ * @param {Object} opts
+ * @api public
+ */
+
+function Backoff(opts) {
+  opts = opts || {};
+  this.ms = opts.min || 100;
+  this.max = opts.max || 10000;
+  this.factor = opts.factor || 2;
+  this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0;
+  this.attempts = 0;
+}
+
+/**
+ * Return the backoff duration.
+ *
+ * @return {Number}
+ * @api public
+ */
+
+Backoff.prototype.duration = function(){
+  var ms = this.ms * Math.pow(this.factor, this.attempts++);
+  if (this.jitter) {
+    var rand =  Math.random();
+    var deviation = Math.floor(rand * this.jitter * ms);
+    ms = (Math.floor(rand * 10) & 1) == 0  ? ms - deviation : ms + deviation;
+  }
+  return Math.min(ms, this.max) | 0;
+};
+
+/**
+ * Reset the number of attempts.
+ *
+ * @api public
+ */
+
+Backoff.prototype.reset = function(){
+  this.attempts = 0;
+};
+
+/**
+ * Set the minimum duration
+ *
+ * @api public
+ */
+
+Backoff.prototype.setMin = function(min){
+  this.ms = min;
+};
+
+/**
+ * Set the maximum duration
+ *
+ * @api public
+ */
+
+Backoff.prototype.setMax = function(max){
+  this.max = max;
+};
+
+/**
+ * Set the jitter
+ *
+ * @api public
+ */
+
+Backoff.prototype.setJitter = function(jitter){
+  this.jitter = jitter;
+};
+
+
+},{}],8:[function(_dereq_,module,exports){
+/**
+ * Slice reference.
+ */
+
+var slice = [].slice;
+
+/**
+ * Bind `obj` to `fn`.
+ *
+ * @param {Object} obj
+ * @param {Function|String} fn or string
+ * @return {Function}
+ * @api public
+ */
+
+module.exports = function(obj, fn){
+  if ('string' == typeof fn) fn = obj[fn];
+  if ('function' != typeof fn) throw new Error('bind() requires a function');
+  var args = slice.call(arguments, 2);
+  return function(){
+    return fn.apply(obj, args.concat(slice.call(arguments)));
+  }
+};
+
+},{}],9:[function(_dereq_,module,exports){
+
+/**
+ * Expose `Emitter`.
+ */
+
+module.exports = Emitter;
+
+/**
+ * Initialize a new `Emitter`.
+ *
+ * @api public
+ */
+
+function Emitter(obj) {
+  if (obj) return mixin(obj);
+};
+
+/**
+ * Mixin the emitter properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+function mixin(obj) {
+  for (var key in Emitter.prototype) {
+    obj[key] = Emitter.prototype[key];
+  }
+  return obj;
+}
+
+/**
+ * Listen on the given `event` with `fn`.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.on =
+Emitter.prototype.addEventListener = function(event, fn){
+  this._callbacks = this._callbacks || {};
+  (this._callbacks[event] = this._callbacks[event] || [])
+    .push(fn);
+  return this;
+};
+
+/**
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.once = function(event, fn){
+  var self = this;
+  this._callbacks = this._callbacks || {};
+
+  function on() {
+    self.off(event, on);
+    fn.apply(this, arguments);
+  }
+
+  on.fn = fn;
+  this.on(event, on);
+  return this;
+};
+
+/**
+ * Remove the given callback for `event` or all
+ * registered callbacks.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.off =
+Emitter.prototype.removeListener =
+Emitter.prototype.removeAllListeners =
+Emitter.prototype.removeEventListener = function(event, fn){
+  this._callbacks = this._callbacks || {};
+
+  // all
+  if (0 == arguments.length) {
+    this._callbacks = {};
+    return this;
+  }
+
+  // specific event
+  var callbacks = this._callbacks[event];
+  if (!callbacks) return this;
+
+  // remove all handlers
+  if (1 == arguments.length) {
+    delete this._callbacks[event];
+    return this;
+  }
+
+  // remove specific handler
+  var cb;
+  for (var i = 0; i < callbacks.length; i++) {
+    cb = callbacks[i];
+    if (cb === fn || cb.fn === fn) {
+      callbacks.splice(i, 1);
+      break;
+    }
+  }
+  return this;
+};
+
+/**
+ * Emit `event` with the given args.
+ *
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
+ */
+
+Emitter.prototype.emit = function(event){
+  this._callbacks = this._callbacks || {};
+  var args = [].slice.call(arguments, 1)
+    , callbacks = this._callbacks[event];
+
+  if (callbacks) {
+    callbacks = callbacks.slice(0);
+    for (var i = 0, len = callbacks.length; i < len; ++i) {
+      callbacks[i].apply(this, args);
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Return array of callbacks for `event`.
+ *
+ * @param {String} event
+ * @return {Array}
+ * @api public
+ */
+
+Emitter.prototype.listeners = function(event){
+  this._callbacks = this._callbacks || {};
+  return this._callbacks[event] || [];
+};
+
+/**
+ * Check if this emitter has `event` handlers.
+ *
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
+ */
+
+Emitter.prototype.hasListeners = function(event){
+  return !! this.listeners(event).length;
+};
+
+},{}],10:[function(_dereq_,module,exports){
+
+/**
+ * Expose `debug()` as the module.
+ */
+
+module.exports = debug;
+
+/**
+ * Create a debugger with the given `name`.
+ *
+ * @param {String} name
+ * @return {Type}
+ * @api public
+ */
+
+function debug(name) {
+  if (!debug.enabled(name)) return function(){};
+
+  return function(fmt){
+    fmt = coerce(fmt);
+
+    var curr = new Date;
+    var ms = curr - (debug[name] || curr);
+    debug[name] = curr;
+
+    fmt = name
+      + ' '
+      + fmt
+      + ' +' + debug.humanize(ms);
+
+    // This hackery is required for IE8
+    // where `console.log` doesn't have 'apply'
+    window.console
+      && console.log
+      && Function.prototype.apply.call(console.log, console, arguments);
+  }
+}
+
+/**
+ * The currently active debug mode names.
+ */
+
+debug.names = [];
+debug.skips = [];
+
+/**
+ * Enables a debug mode by name. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} name
+ * @api public
+ */
+
+debug.enable = function(name) {
+  try {
+    localStorage.debug = name;
+  } catch(e){}
+
+  var split = (name || '').split(/[\s,]+/)
+    , len = split.length;
+
+  for (var i = 0; i < len; i++) {
+    name = split[i].replace('*', '.*?');
+    if (name[0] === '-') {
+      debug.skips.push(new RegExp('^' + name.substr(1) + '$'));
+    }
+    else {
+      debug.names.push(new RegExp('^' + name + '$'));
+    }
+  }
+};
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+debug.disable = function(){
+  debug.enable('');
+};
+
+/**
+ * Humanize the given `ms`.
+ *
+ * @param {Number} m
+ * @return {String}
+ * @api private
+ */
+
+debug.humanize = function(ms) {
+  var sec = 1000
+    , min = 60 * 1000
+    , hour = 60 * min;
+
+  if (ms >= hour) return (ms / hour).toFixed(1) + 'h';
+  if (ms >= min) return (ms / min).toFixed(1) + 'm';
+  if (ms >= sec) return (ms / sec | 0) + 's';
+  return ms + 'ms';
+};
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+debug.enabled = function(name) {
+  for (var i = 0, len = debug.skips.length; i < len; i++) {
+    if (debug.skips[i].test(name)) {
+      return false;
+    }
+  }
+  for (var i = 0, len = debug.names.length; i < len; i++) {
+    if (debug.names[i].test(name)) {
+      return true;
+    }
+  }
+  return false;
+};
+
+/**
+ * Coerce `val`.
+ */
+
+function coerce(val) {
+  if (val instanceof Error) return val.stack || val.message;
+  return val;
+}
+
+// persist
+
+try {
+  if (window.localStorage) debug.enable(localStorage.debug);
+} catch(e){}
+
+},{}],11:[function(_dereq_,module,exports){
+
+module.exports =  _dereq_('./lib/');
+
+},{"./lib/":12}],12:[function(_dereq_,module,exports){
+
+module.exports = _dereq_('./socket');
+
+/**
+ * Exports parser
+ *
+ * @api public
+ *
+ */
+module.exports.parser = _dereq_('engine.io-parser');
+
+},{"./socket":13,"engine.io-parser":25}],13:[function(_dereq_,module,exports){
+(function (global){
+/**
+ * Module dependencies.
+ */
+
+var transports = _dereq_('./transports');
+var Emitter = _dereq_('component-emitter');
+var debug = _dereq_('debug')('engine.io-client:socket');
+var index = _dereq_('indexof');
+var parser = _dereq_('engine.io-parser');
+var parseuri = _dereq_('parseuri');
+var parsejson = _dereq_('parsejson');
+var parseqs = _dereq_('parseqs');
+
+/**
+ * Module exports.
+ */
+
+module.exports = Socket;
+
+/**
+ * Noop function.
+ *
+ * @api private
+ */
+
+function noop(){}
+
+/**
+ * Socket constructor.
+ *
+ * @param {String|Object} uri or options
+ * @param {Object} options
+ * @api public
+ */
+
+function Socket(uri, opts){
+  if (!(this instanceof Socket)) return new Socket(uri, opts);
+
+  opts = opts || {};
+
+  if (uri && 'object' == typeof uri) {
+    opts = uri;
+    uri = null;
+  }
+
+  if (uri) {
+    uri = parseuri(uri);
+    opts.host = uri.host;
+    opts.secure = uri.protocol == 'https' || uri.protocol == 'wss';
+    opts.port = uri.port;
+    if (uri.query) opts.query = uri.query;
+  }
+
+  this.secure = null != opts.secure ? opts.secure :
+    (global.location && 'https:' == location.protocol);
+
+  if (opts.host) {
+    var pieces = opts.host.split(':');
+    opts.hostname = pieces.shift();
+    if (pieces.length) {
+      opts.port = pieces.pop();
+    } else if (!opts.port) {
+      // if no port is specified manually, use the protocol default
+      opts.port = this.secure ? '443' : '80';
+    }
+  }
+
+  this.agent = opts.agent || false;
+  this.hostname = opts.hostname ||
+    (global.location ? location.hostname : 'localhost');
+  this.port = opts.port || (global.location && location.port ?
+       location.port :
+       (this.secure ? 443 : 80));
+  this.query = opts.query || {};
+  if ('string' == typeof this.query) this.query = parseqs.decode(this.query);
+  this.upgrade = false !== opts.upgrade;
+  this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
+  this.forceJSONP = !!opts.forceJSONP;
+  this.jsonp = false !== opts.jsonp;
+  this.forceBase64 = !!opts.forceBase64;
+  this.enablesXDR = !!opts.enablesXDR;
+  this.timestampParam = opts.timestampParam || 't';
+  this.timestampRequests = opts.timestampRequests;
+  this.transports = opts.transports || ['polling', 'websocket'];
+  this.readyState = '';
+  this.writeBuffer = [];
+  this.callbackBuffer = [];
+  this.policyPort = opts.policyPort || 843;
+  this.rememberUpgrade = opts.rememberUpgrade || false;
+  this.binaryType = null;
+  this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
+
+  // SSL options for Node.js client
+  this.pfx = opts.pfx || null;
+  this.key = opts.key || null;
+  this.passphrase = opts.passphrase || null;
+  this.cert = opts.cert || null;
+  this.ca = opts.ca || null;
+  this.ciphers = opts.ciphers || null;
+  this.rejectUnauthorized = opts.rejectUnauthorized || null;
+
+  this.open();
+}
+
+Socket.priorWebsocketSuccess = false;
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Socket.prototype);
+
+/**
+ * Protocol version.
+ *
+ * @api public
+ */
+
+Socket.protocol = parser.protocol; // this is an int
+
+/**
+ * Expose deps for legacy compatibility
+ * and standalone browser access.
+ */
+
+Socket.Socket = Socket;
+Socket.Transport = _dereq_('./transport');
+Socket.transports = _dereq_('./transports');
+Socket.parser = _dereq_('engine.io-parser');
+
+/**
+ * Creates transport of the given type.
+ *
+ * @param {String} transport name
+ * @return {Transport}
+ * @api private
+ */
+
+Socket.prototype.createTransport = function (name) {
+  debug('creating transport "%s"', name);
+  var query = clone(this.query);
+
+  // append engine.io protocol identifier
+  query.EIO = parser.protocol;
+
+  // transport name
+  query.transport = name;
+
+  // session id if we already have one
+  if (this.id) query.sid = this.id;
+
+  var transport = new transports[name]({
+    agent: this.agent,
+    hostname: this.hostname,
+    port: this.port,
+    secure: this.secure,
+    path: this.path,
+    query: query,
+    forceJSONP: this.forceJSONP,
+    jsonp: this.jsonp,
+    forceBase64: this.forceBase64,
+    enablesXDR: this.enablesXDR,
+    timestampRequests: this.timestampRequests,
+    timestampParam: this.timestampParam,
+    policyPort: this.policyPort,
+    socket: this,
+    pfx: this.pfx,
+    key: this.key,
+    passphrase: this.passphrase,
+    cert: this.cert,
+    ca: this.ca,
+    ciphers: this.ciphers,
+    rejectUnauthorized: this.rejectUnauthorized
+  });
+
+  return transport;
+};
+
+function clone (obj) {
+  var o = {};
+  for (var i in obj) {
+    if (obj.hasOwnProperty(i)) {
+      o[i] = obj[i];
+    }
+  }
+  return o;
+}
+
+/**
+ * Initializes transport to use and starts probe.
+ *
+ * @api private
+ */
+Socket.prototype.open = function () {
+  var transport;
+  if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) {
+    transport = 'websocket';
+  } else if (0 == this.transports.length) {
+    // Emit error on next tick so it can be listened to
+    var self = this;
+    setTimeout(function() {
+      self.emit('error', 'No transports available');
+    }, 0);
+    return;
+  } else {
+    transport = this.transports[0];
+  }
+  this.readyState = 'opening';
+
+  // Retry with the next transport if the transport is disabled (jsonp: false)
+  var transport;
+  try {
+    transport = this.createTransport(transport);
+  } catch (e) {
+    this.transports.shift();
+    this.open();
+    return;
+  }
+
+  transport.open();
+  this.setTransport(transport);
+};
+
+/**
+ * Sets the current transport. Disables the existing one (if any).
+ *
+ * @api private
+ */
+
+Socket.prototype.setTransport = function(transport){
+  debug('setting transport %s', transport.name);
+  var self = this;
+
+  if (this.transport) {
+    debug('clearing existing transport %s', this.transport.name);
+    this.transport.removeAllListeners();
+  }
+
+  // set up transport
+  this.transport = transport;
+
+  // set up transport listeners
+  transport
+  .on('drain', function(){
+    self.onDrain();
+  })
+  .on('packet', function(packet){
+    self.onPacket(packet);
+  })
+  .on('error', function(e){
+    self.onError(e);
+  })
+  .on('close', function(){
+    self.onClose('transport close');
+  });
+};
+
+/**
+ * Probes a transport.
+ *
+ * @param {String} transport name
+ * @api private
+ */
+
+Socket.prototype.probe = function (name) {
+  debug('probing transport "%s"', name);
+  var transport = this.createTransport(name, { probe: 1 })
+    , failed = false
+    , self = this;
+
+  Socket.priorWebsocketSuccess = false;
+
+  function onTransportOpen(){
+    if (self.onlyBinaryUpgrades) {
+      var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
+      failed = failed || upgradeLosesBinary;
+    }
+    if (failed) return;
+
+    debug('probe transport "%s" opened', name);
+    transport.send([{ type: 'ping', data: 'probe' }]);
+    transport.once('packet', function (msg) {
+      if (failed) return;
+      if ('pong' == msg.type && 'probe' == msg.data) {
+        debug('probe transport "%s" pong', name);
+        self.upgrading = true;
+        self.emit('upgrading', transport);
+        if (!transport) return;
+        Socket.priorWebsocketSuccess = 'websocket' == transport.name;
+
+        debug('pausing current transport "%s"', self.transport.name);
+        self.transport.pause(function () {
+          if (failed) return;
+          if ('closed' == self.readyState) return;
+          debug('changing transport and sending upgrade packet');
+
+          cleanup();
+
+          self.setTransport(transport);
+          transport.send([{ type: 'upgrade' }]);
+          self.emit('upgrade', transport);
+          transport = null;
+          self.upgrading = false;
+          self.flush();
+        });
+      } else {
+        debug('probe transport "%s" failed', name);
+        var err = new Error('probe error');
+        err.transport = transport.name;
+        self.emit('upgradeError', err);
+      }
+    });
+  }
+
+  function freezeTransport() {
+    if (failed) return;
+
+    // Any callback called by transport should be ignored since now
+    failed = true;
+
+    cleanup();
+
+    transport.close();
+    transport = null;
+  }
+
+  //Handle any error that happens while probing
+  function onerror(err) {
+    var error = new Error('probe error: ' + err);
+    error.transport = transport.name;
+
+    freezeTransport();
+
+    debug('probe transport "%s" failed because of error: %s', name, err);
+
+    self.emit('upgradeError', error);
+  }
+
+  function onTransportClose(){
+    onerror("transport closed");
+  }
+
+  //When the socket is closed while we're probing
+  function onclose(){
+    onerror("socket closed");
+  }
+
+  //When the socket is upgraded while we're probing
+  function onupgrade(to){
+    if (transport && to.name != transport.name) {
+      debug('"%s" works - aborting "%s"', to.name, transport.name);
+      freezeTransport();
+    }
+  }
+
+  //Remove all listeners on the transport and on self
+  function cleanup(){
+    transport.removeListener('open', onTransportOpen);
+    transport.removeListener('error', onerror);
+    transport.removeListener('close', onTransportClose);
+    self.removeListener('close', onclose);
+    self.removeListener('upgrading', onupgrade);
+  }
+
+  transport.once('open', onTransportOpen);
+  transport.once('error', onerror);
+  transport.once('close', onTransportClose);
+
+  this.once('close', onclose);
+  this.once('upgrading', onupgrade);
+
+  transport.open();
+
+};
+
+/**
+ * Called when connection is deemed open.
+ *
+ * @api public
+ */
+
+Socket.prototype.onOpen = function () {
+  debug('socket open');
+  this.readyState = 'open';
+  Socket.priorWebsocketSuccess = 'websocket' == this.transport.name;
+  this.emit('open');
+  this.flush();
+
+  // we check for `readyState` in case an `open`
+  // listener already closed the socket
+  if ('open' == this.readyState && this.upgrade && this.transport.pause) {
+    debug('starting upgrade probes');
+    for (var i = 0, l = this.upgrades.length; i < l; i++) {
+      this.probe(this.upgrades[i]);
+    }
+  }
+};
+
+/**
+ * Handles a packet.
+ *
+ * @api private
+ */
+
+Socket.prototype.onPacket = function (packet) {
+  if ('opening' == this.readyState || 'open' == this.readyState) {
+    debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
+
+    this.emit('packet', packet);
+
+    // Socket is live - any packet counts
+    this.emit('heartbeat');
+
+    switch (packet.type) {
+      case 'open':
+        this.onHandshake(parsejson(packet.data));
+        break;
+
+      case 'pong':
+        this.setPing();
+        break;
+
+      case 'error':
+        var err = new Error('server error');
+        err.code = packet.data;
+        this.emit('error', err);
+        break;
+
+      case 'message':
+        this.emit('data', packet.data);
+        this.emit('message', packet.data);
+        break;
+    }
+  } else {
+    debug('packet received with socket readyState "%s"', this.readyState);
+  }
+};
+
+/**
+ * Called upon handshake completion.
+ *
+ * @param {Object} handshake obj
+ * @api private
+ */
+
+Socket.prototype.onHandshake = function (data) {
+  this.emit('handshake', data);
+  this.id = data.sid;
+  this.transport.query.sid = data.sid;
+  this.upgrades = this.filterUpgrades(data.upgrades);
+  this.pingInterval = data.pingInterval;
+  this.pingTimeout = data.pingTimeout;
+  this.onOpen();
+  // In case open handler closes socket
+  if  ('closed' == this.readyState) return;
+  this.setPing();
+
+  // Prolong liveness of socket on heartbeat
+  this.removeListener('heartbeat', this.onHeartbeat);
+  this.on('heartbeat', this.onHeartbeat);
+};
+
+/**
+ * Resets ping timeout.
+ *
+ * @api private
+ */
+
+Socket.prototype.onHeartbeat = function (timeout) {
+  clearTimeout(this.pingTimeoutTimer);
+  var self = this;
+  self.pingTimeoutTimer = setTimeout(function () {
+    if ('closed' == self.readyState) return;
+    self.onClose('ping timeout');
+  }, timeout || (self.pingInterval + self.pingTimeout));
+};
+
+/**
+ * Pings server every `this.pingInterval` and expects response
+ * within `this.pingTimeout` or closes connection.
+ *
+ * @api private
+ */
+
+Socket.prototype.setPing = function () {
+  var self = this;
+  clearTimeout(self.pingIntervalTimer);
+  self.pingIntervalTimer = setTimeout(function () {
+    debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
+    self.ping();
+    self.onHeartbeat(self.pingTimeout);
+  }, self.pingInterval);
+};
+
+/**
+* Sends a ping packet.
+*
+* @api public
+*/
+
+Socket.prototype.ping = function () {
+  this.sendPacket('ping');
+};
+
+/**
+ * Called on `drain` event
+ *
+ * @api private
+ */
+
+Socket.prototype.onDrain = function() {
+  for (var i = 0; i < this.prevBufferLen; i++) {
+    if (this.callbackBuffer[i]) {
+      this.callbackBuffer[i]();
+    }
+  }
+
+  this.writeBuffer.splice(0, this.prevBufferLen);
+  this.callbackBuffer.splice(0, this.prevBufferLen);
+
+  // setting prevBufferLen = 0 is very important
+  // for example, when upgrading, upgrade packet is sent over,
+  // and a nonzero prevBufferLen could cause problems on `drain`
+  this.prevBufferLen = 0;
+
+  if (this.writeBuffer.length == 0) {
+    this.emit('drain');
+  } else {
+    this.flush();
+  }
+};
+
+/**
+ * Flush write buffers.
+ *
+ * @api private
+ */
+
+Socket.prototype.flush = function () {
+  if ('closed' != this.readyState && this.transport.writable &&
+    !this.upgrading && this.writeBuffer.length) {
+    debug('flushing %d packets in socket', this.writeBuffer.length);
+    this.transport.send(this.writeBuffer);
+    // keep track of current length of writeBuffer
+    // splice writeBuffer and callbackBuffer on `drain`
+    this.prevBufferLen = this.writeBuffer.length;
+    this.emit('flush');
+  }
+};
+
+/**
+ * Sends a message.
+ *
+ * @param {String} message.
+ * @param {Function} callback function.
+ * @return {Socket} for chaining.
+ * @api public
+ */
+
+Socket.prototype.write =
+Socket.prototype.send = function (msg, fn) {
+  this.sendPacket('message', msg, fn);
+  return this;
+};
+
+/**
+ * Sends a packet.
+ *
+ * @param {String} packet type.
+ * @param {String} data.
+ * @param {Function} callback function.
+ * @api private
+ */
+
+Socket.prototype.sendPacket = function (type, data, fn) {
+  if ('closing' == this.readyState || 'closed' == this.readyState) {
+    return;
+  }
+
+  var packet = { type: type, data: data };
+  this.emit('packetCreate', packet);
+  this.writeBuffer.push(packet);
+  this.callbackBuffer.push(fn);
+  this.flush();
+};
+
+/**
+ * Closes the connection.
+ *
+ * @api private
+ */
+
+Socket.prototype.close = function () {
+  if ('opening' == this.readyState || 'open' == this.readyState) {
+    this.readyState = 'closing';
+
+    var self = this;
+
+    function close() {
+      self.onClose('forced close');
+      debug('socket closing - telling transport to close');
+      self.transport.close();
+    }
+
+    function cleanupAndClose() {
+      self.removeListener('upgrade', cleanupAndClose);
+      self.removeListener('upgradeError', cleanupAndClose);
+      close();
+    }
+
+    function waitForUpgrade() {
+      // wait for upgrade to finish since we can't send packets while pausing a transport
+      self.once('upgrade', cleanupAndClose);
+      self.once('upgradeError', cleanupAndClose);
+    }
+
+    if (this.writeBuffer.length) {
+      this.once('drain', function() {
+        if (this.upgrading) {
+          waitForUpgrade();
+        } else {
+          close();
+        }
+      });
+    } else if (this.upgrading) {
+      waitForUpgrade();
+    } else {
+      close();
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Called upon transport error
+ *
+ * @api private
+ */
+
+Socket.prototype.onError = function (err) {
+  debug('socket error %j', err);
+  Socket.priorWebsocketSuccess = false;
+  this.emit('error', err);
+  this.onClose('transport error', err);
+};
+
+/**
+ * Called upon transport close.
+ *
+ * @api private
+ */
+
+Socket.prototype.onClose = function (reason, desc) {
+  if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) {
+    debug('socket close with reason: "%s"', reason);
+    var self = this;
+
+    // clear timers
+    clearTimeout(this.pingIntervalTimer);
+    clearTimeout(this.pingTimeoutTimer);
+
+    // clean buffers in next tick, so developers can still
+    // grab the buffers on `close` event
+    setTimeout(function() {
+      self.writeBuffer = [];
+      self.callbackBuffer = [];
+      self.prevBufferLen = 0;
+    }, 0);
+
+    // stop event from firing again for transport
+    this.transport.removeAllListeners('close');
+
+    // ensure transport won't stay open
+    this.transport.close();
+
+    // ignore further transport communication
+    this.transport.removeAllListeners();
+
+    // set ready state
+    this.readyState = 'closed';
+
+    // clear session id
+    this.id = null;
+
+    // emit close event
+    this.emit('close', reason, desc);
+  }
+};
+
+/**
+ * Filters upgrades, returning only those matching client transports.
+ *
+ * @param {Array} server upgrades
+ * @api private
+ *
+ */
+
+Socket.prototype.filterUpgrades = function (upgrades) {
+  var filteredUpgrades = [];
+  for (var i = 0, j = upgrades.length; i<j; i++) {
+    if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
+  }
+  return filteredUpgrades;
+};
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./transport":14,"./transports":15,"component-emitter":9,"debug":22,"engine.io-parser":25,"indexof":42,"parsejson":34,"parseqs":35,"parseuri":36}],14:[function(_dereq_,module,exports){
+/**
+ * Module dependencies.
+ */
+
+var parser = _dereq_('engine.io-parser');
+var Emitter = _dereq_('component-emitter');
+
+/**
+ * Module exports.
+ */
+
+module.exports = Transport;
+
+/**
+ * Transport abstract constructor.
+ *
+ * @param {Object} options.
+ * @api private
+ */
+
+function Transport (opts) {
+  this.path = opts.path;
+  this.hostname = opts.hostname;
+  this.port = opts.port;
+  this.secure = opts.secure;
+  this.query = opts.query;
+  this.timestampParam = opts.timestampParam;
+  this.timestampRequests = opts.timestampRequests;
+  this.readyState = '';
+  this.agent = opts.agent || false;
+  this.socket = opts.socket;
+  this.enablesXDR = opts.enablesXDR;
+
+  // SSL options for Node.js client
+  this.pfx = opts.pfx;
+  this.key = opts.key;
+  this.passphrase = opts.passphrase;
+  this.cert = opts.cert;
+  this.ca = opts.ca;
+  this.ciphers = opts.ciphers;
+  this.rejectUnauthorized = opts.rejectUnauthorized;
+}
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Transport.prototype);
+
+/**
+ * A counter used to prevent collisions in the timestamps used
+ * for cache busting.
+ */
+
+Transport.timestamps = 0;
+
+/**
+ * Emits an error.
+ *
+ * @param {String} str
+ * @return {Transport} for chaining
+ * @api public
+ */
+
+Transport.prototype.onError = function (msg, desc) {
+  var err = new Error(msg);
+  err.type = 'TransportError';
+  err.description = desc;
+  this.emit('error', err);
+  return this;
+};
+
+/**
+ * Opens the transport.
+ *
+ * @api public
+ */
+
+Transport.prototype.open = function () {
+  if ('closed' == this.readyState || '' == this.readyState) {
+    this.readyState = 'opening';
+    this.doOpen();
+  }
+
+  return this;
+};
+
+/**
+ * Closes the transport.
+ *
+ * @api private
+ */
+
+Transport.prototype.close = function () {
+  if ('opening' == this.readyState || 'open' == this.readyState) {
+    this.doClose();
+    this.onClose();
+  }
+
+  return this;
+};
+
+/**
+ * Sends multiple packets.
+ *
+ * @param {Array} packets
+ * @api private
+ */
+
+Transport.prototype.send = function(packets){
+  if ('open' == this.readyState) {
+    this.write(packets);
+  } else {
+    throw new Error('Transport not open');
+  }
+};
+
+/**
+ * Called upon open
+ *
+ * @api private
+ */
+
+Transport.prototype.onOpen = function () {
+  this.readyState = 'open';
+  this.writable = true;
+  this.emit('open');
+};
+
+/**
+ * Called with data.
+ *
+ * @param {String} data
+ * @api private
+ */
+
+Transport.prototype.onData = function(data){
+  var packet = parser.decodePacket(data, this.socket.binaryType);
+  this.onPacket(packet);
+};
+
+/**
+ * Called with a decoded packet.
+ */
+
+Transport.prototype.onPacket = function (packet) {
+  this.emit('packet', packet);
+};
+
+/**
+ * Called upon close.
+ *
+ * @api private
+ */
+
+Transport.prototype.onClose = function () {
+  this.readyState = 'closed';
+  this.emit('close');
+};
+
+},{"component-emitter":9,"engine.io-parser":25}],15:[function(_dereq_,module,exports){
+(function (global){
+/**
+ * Module dependencies
+ */
+
+var XMLHttpRequest = _dereq_('xmlhttprequest');
+var XHR = _dereq_('./polling-xhr');
+var JSONP = _dereq_('./polling-jsonp');
+var websocket = _dereq_('./websocket');
+
+/**
+ * Export transports.
+ */
+
+exports.polling = polling;
+exports.websocket = websocket;
+
+/**
+ * Polling transport polymorphic constructor.
+ * Decides on xhr vs jsonp based on feature detection.
+ *
+ * @api private
+ */
+
+function polling(opts){
+  var xhr;
+  var xd = false;
+  var xs = false;
+  var jsonp = false !== opts.jsonp;
+
+  if (global.location) {
+    var isSSL = 'https:' == location.protocol;
+    var port = location.port;
+
+    // some user agents have empty `location.port`
+    if (!port) {
+      port = isSSL ? 443 : 80;
+    }
+
+    xd = opts.hostname != location.hostname || port != opts.port;
+    xs = opts.secure != isSSL;
+  }
+
+  opts.xdomain = xd;
+  opts.xscheme = xs;
+  xhr = new XMLHttpRequest(opts);
+
+  if ('open' in xhr && !opts.forceJSONP) {
+    return new XHR(opts);
+  } else {
+    if (!jsonp) throw new Error('JSONP disabled');
+    return new JSONP(opts);
+  }
+}
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./polling-jsonp":16,"./polling-xhr":17,"./websocket":19,"xmlhttprequest":20}],16:[function(_dereq_,module,exports){
+(function (global){
+
+/**
+ * Module requirements.
+ */
+
+var Polling = _dereq_('./polling');
+var inherit = _dereq_('component-inherit');
+
+/**
+ * Module exports.
+ */
+
+module.exports = JSONPPolling;
+
+/**
+ * Cached regular expressions.
+ */
+
+var rNewline = /\n/g;
+var rEscapedNewline = /\\n/g;
+
+/**
+ * Global JSONP callbacks.
+ */
+
+var callbacks;
+
+/**
+ * Callbacks count.
+ */
+
+var index = 0;
+
+/**
+ * Noop.
+ */
+
+function empty () { }
+
+/**
+ * JSONP Polling constructor.
+ *
+ * @param {Object} opts.
+ * @api public
+ */
+
+function JSONPPolling (opts) {
+  Polling.call(this, opts);
+
+  this.query = this.query || {};
+
+  // define global callbacks array if not present
+  // we do this here (lazily) to avoid unneeded global pollution
+  if (!callbacks) {
+    // we need to consider multiple engines in the same page
+    if (!global.___eio) global.___eio = [];
+    callbacks = global.___eio;
+  }
+
+  // callback identifier
+  this.index = callbacks.length;
+
+  // add callback to jsonp global
+  var self = this;
+  callbacks.push(function (msg) {
+    self.onData(msg);
+  });
+
+  // append to query string
+  this.query.j = this.index;
+
+  // prevent spurious errors from being emitted when the window is unloaded
+  if (global.document && global.addEventListener) {
+    global.addEventListener('beforeunload', function () {
+      if (self.script) self.script.onerror = empty;
+    }, false);
+  }
+}
+
+/**
+ * Inherits from Polling.
+ */
+
+inherit(JSONPPolling, Polling);
+
+/*
+ * JSONP only supports binary as base64 encoded strings
+ */
+
+JSONPPolling.prototype.supportsBinary = false;
+
+/**
+ * Closes the socket.
+ *
+ * @api private
+ */
+
+JSONPPolling.prototype.doClose = function () {
+  if (this.script) {
+    this.script.parentNode.removeChild(this.script);
+    this.script = null;
+  }
+
+  if (this.form) {
+    this.form.parentNode.removeChild(this.form);
+    this.form = null;
+    this.iframe = null;
+  }
+
+  Polling.prototype.doClose.call(this);
+};
+
+/**
+ * Starts a poll cycle.
+ *
+ * @api private
+ */
+
+JSONPPolling.prototype.doPoll = function () {
+  var self = this;
+  var script = document.createElement('script');
+
+  if (this.script) {
+    this.script.parentNode.removeChild(this.script);
+    this.script = null;
+  }
+
+  script.async = true;
+  script.src = this.uri();
+  script.onerror = function(e){
+    self.onError('jsonp poll error',e);
+  };
+
+  var insertAt = document.getElementsByTagName('script')[0];
+  insertAt.parentNode.insertBefore(script, insertAt);
+  this.script = script;
+
+  var isUAgecko = 'undefined' != typeof navigator && /gecko/i.test(navigator.userAgent);
+  
+  if (isUAgecko) {
+    setTimeout(function () {
+      var iframe = document.createElement('iframe');
+      document.body.appendChild(iframe);
+      document.body.removeChild(iframe);
+    }, 100);
+  }
+};
+
+/**
+ * Writes with a hidden iframe.
+ *
+ * @param {String} data to send
+ * @param {Function} called upon flush.
+ * @api private
+ */
+
+JSONPPolling.prototype.doWrite = function (data, fn) {
+  var self = this;
+
+  if (!this.form) {
+    var form = document.createElement('form');
+    var area = document.createElement('textarea');
+    var id = this.iframeId = 'eio_iframe_' + this.index;
+    var iframe;
+
+    form.className = 'socketio';
+    form.style.position = 'absolute';
+    form.style.top = '-1000px';
+    form.style.left = '-1000px';
+    form.target = id;
+    form.method = 'POST';
+    form.setAttribute('accept-charset', 'utf-8');
+    area.name = 'd';
+    form.appendChild(area);
+    document.body.appendChild(form);
+
+    this.form = form;
+    this.area = area;
+  }
+
+  this.form.action = this.uri();
+
+  function complete () {
+    initIframe();
+    fn();
+  }
+
+  function initIframe () {
+    if (self.iframe) {
+      try {
+        self.form.removeChild(self.iframe);
+      } catch (e) {
+        self.onError('jsonp polling iframe removal error', e);
+      }
+    }
+
+    try {
+      // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
+      var html = '<iframe src="javascript:0" name="'+ self.iframeId +'">';
+      iframe = document.createElement(html);
+    } catch (e) {
+      iframe = document.createElement('iframe');
+      iframe.name = self.iframeId;
+      iframe.src = 'javascript:0';
+    }
+
+    iframe.id = self.iframeId;
+
+    self.form.appendChild(iframe);
+    self.iframe = iframe;
+  }
+
+  initIframe();
+
+  // escape \n to prevent it from being converted into \r\n by some UAs
+  // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
+  data = data.replace(rEscapedNewline, '\\\n');
+  this.area.value = data.replace(rNewline, '\\n');
+
+  try {
+    this.form.submit();
+  } catch(e) {}
+
+  if (this.iframe.attachEvent) {
+    this.iframe.onreadystatechange = function(){
+      if (self.iframe.readyState == 'complete') {
+        complete();
+      }
+    };
+  } else {
+    this.iframe.onload = complete;
+  }
+};
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./polling":18,"component-inherit":21}],17:[function(_dereq_,module,exports){
+(function (global){
+/**
+ * Module requirements.
+ */
+
+var XMLHttpRequest = _dereq_('xmlhttprequest');
+var Polling = _dereq_('./polling');
+var Emitter = _dereq_('component-emitter');
+var inherit = _dereq_('component-inherit');
+var debug = _dereq_('debug')('engine.io-client:polling-xhr');
+
+/**
+ * Module exports.
+ */
+
+module.exports = XHR;
+module.exports.Request = Request;
+
+/**
+ * Empty function
+ */
+
+function empty(){}
+
+/**
+ * XHR Polling constructor.
+ *
+ * @param {Object} opts
+ * @api public
+ */
+
+function XHR(opts){
+  Polling.call(this, opts);
+
+  if (global.location) {
+    var isSSL = 'https:' == location.protocol;
+    var port = location.port;
+
+    // some user agents have empty `location.port`
+    if (!port) {
+      port = isSSL ? 443 : 80;
+    }
+
+    this.xd = opts.hostname != global.location.hostname ||
+      port != opts.port;
+    this.xs = opts.secure != isSSL;
+  }
+}
+
+/**
+ * Inherits from Polling.
+ */
+
+inherit(XHR, Polling);
+
+/**
+ * XHR supports binary
+ */
+
+XHR.prototype.supportsBinary = true;
+
+/**
+ * Creates a request.
+ *
+ * @param {String} method
+ * @api private
+ */
+
+XHR.prototype.request = function(opts){
+  opts = opts || {};
+  opts.uri = this.uri();
+  opts.xd = this.xd;
+  opts.xs = this.xs;
+  opts.agent = this.agent || false;
+  opts.supportsBinary = this.supportsBinary;
+  opts.enablesXDR = this.enablesXDR;
+
+  // SSL options for Node.js client
+  opts.pfx = this.pfx;
+  opts.key = this.key;
+  opts.passphrase = this.passphrase;
+  opts.cert = this.cert;
+  opts.ca = this.ca;
+  opts.ciphers = this.ciphers;
+  opts.rejectUnauthorized = this.rejectUnauthorized;
+
+  return new Request(opts);
+};
+
+/**
+ * Sends data.
+ *
+ * @param {String} data to send.
+ * @param {Function} called upon flush.
+ * @api private
+ */
+
+XHR.prototype.doWrite = function(data, fn){
+  var isBinary = typeof data !== 'string' && data !== undefined;
+  var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
+  var self = this;
+  req.on('success', fn);
+  req.on('error', function(err){
+    self.onError('xhr post error', err);
+  });
+  this.sendXhr = req;
+};
+
+/**
+ * Starts a poll cycle.
+ *
+ * @api private
+ */
+
+XHR.prototype.doPoll = function(){
+  debug('xhr poll');
+  var req = this.request();
+  var self = this;
+  req.on('data', function(data){
+    self.onData(data);
+  });
+  req.on('error', function(err){
+    self.onError('xhr poll error', err);
+  });
+  this.pollXhr = req;
+};
+
+/**
+ * Request constructor
+ *
+ * @param {Object} options
+ * @api public
+ */
+
+function Request(opts){
+  this.method = opts.method || 'GET';
+  this.uri = opts.uri;
+  this.xd = !!opts.xd;
+  this.xs = !!opts.xs;
+  this.async = false !== opts.async;
+  this.data = undefined != opts.data ? opts.data : null;
+  this.agent = opts.agent;
+  this.isBinary = opts.isBinary;
+  this.supportsBinary = opts.supportsBinary;
+  this.enablesXDR = opts.enablesXDR;
+
+  // SSL options for Node.js client
+  this.pfx = opts.pfx;
+  this.key = opts.key;
+  this.passphrase = opts.passphrase;
+  this.cert = opts.cert;
+  this.ca = opts.ca;
+  this.ciphers = opts.ciphers;
+  this.rejectUnauthorized = opts.rejectUnauthorized;
+
+  this.create();
+}
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Request.prototype);
+
+/**
+ * Creates the XHR object and sends the request.
+ *
+ * @api private
+ */
+
+Request.prototype.create = function(){
+  var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };
+
+  // SSL options for Node.js client
+  opts.pfx = this.pfx;
+  opts.key = this.key;
+  opts.passphrase = this.passphrase;
+  opts.cert = this.cert;
+  opts.ca = this.ca;
+  opts.ciphers = this.ciphers;
+  opts.rejectUnauthorized = this.rejectUnauthorized;
+
+  var xhr = this.xhr = new XMLHttpRequest(opts);
+  var self = this;
+
+  try {
+    debug('xhr open %s: %s', this.method, this.uri);
+    xhr.open(this.method, this.uri, this.async);
+    if (this.supportsBinary) {
+      // This has to be done after open because Firefox is stupid
+      // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
+      xhr.responseType = 'arraybuffer';
+    }
+
+    if ('POST' == this.method) {
+      try {
+        if (this.isBinary) {
+          xhr.setRequestHeader('Content-type', 'application/octet-stream');
+        } else {
+          xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
+        }
+      } catch (e) {}
+    }
+
+    // ie6 check
+    if ('withCredentials' in xhr) {
+      xhr.withCredentials = true;
+    }
+
+    if (this.hasXDR()) {
+      xhr.onload = function(){
+        self.onLoad();
+      };
+      xhr.onerror = function(){
+        self.onError(xhr.responseText);
+      };
+    } else {
+      xhr.onreadystatechange = function(){
+        if (4 != xhr.readyState) return;
+        if (200 == xhr.status || 1223 == xhr.status) {
+          self.onLoad();
+        } else {
+          // make sure the `error` event handler that's user-set
+          // does not throw in the same tick and gets caught here
+          setTimeout(function(){
+            self.onError(xhr.status);
+          }, 0);
+        }
+      };
+    }
+
+    debug('xhr data %s', this.data);
+    xhr.send(this.data);
+  } catch (e) {
+    // Need to defer since .create() is called directly fhrom the constructor
+    // and thus the 'error' event can only be only bound *after* this exception
+    // occurs.  Therefore, also, we cannot throw here at all.
+    setTimeout(function() {
+      self.onError(e);
+    }, 0);
+    return;
+  }
+
+  if (global.document) {
+    this.index = Request.requestsCount++;
+    Request.requests[this.index] = this;
+  }
+};
+
+/**
+ * Called upon successful response.
+ *
+ * @api private
+ */
+
+Request.prototype.onSuccess = function(){
+  this.emit('success');
+  this.cleanup();
+};
+
+/**
+ * Called if we have data.
+ *
+ * @api private
+ */
+
+Request.prototype.onData = function(data){
+  this.emit('data', data);
+  this.onSuccess();
+};
+
+/**
+ * Called upon error.
+ *
+ * @api private
+ */
+
+Request.prototype.onError = function(err){
+  this.emit('error', err);
+  this.cleanup(true);
+};
+
+/**
+ * Cleans up house.
+ *
+ * @api private
+ */
+
+Request.prototype.cleanup = function(fromError){
+  if ('undefined' == typeof this.xhr || null === this.xhr) {
+    return;
+  }
+  // xmlhttprequest
+  if (this.hasXDR()) {
+    this.xhr.onload = this.xhr.onerror = empty;
+  } else {
+    this.xhr.onreadystatechange = empty;
+  }
+
+  if (fromError) {
+    try {
+      this.xhr.abort();
+    } catch(e) {}
+  }
+
+  if (global.document) {
+    delete Request.requests[this.index];
+  }
+
+  this.xhr = null;
+};
+
+/**
+ * Called upon load.
+ *
+ * @api private
+ */
+
+Request.prototype.onLoad = function(){
+  var data;
+  try {
+    var contentType;
+    try {
+      contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0];
+    } catch (e) {}
+    if (contentType === 'application/octet-stream') {
+      data = this.xhr.response;
+    } else {
+      if (!this.supportsBinary) {
+        data = this.xhr.responseText;
+      } else {
+        data = 'ok';
+      }
+    }
+  } catch (e) {
+    this.onError(e);
+  }
+  if (null != data) {
+    this.onData(data);
+  }
+};
+
+/**
+ * Check if it has XDomainRequest.
+ *
+ * @api private
+ */
+
+Request.prototype.hasXDR = function(){
+  return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR;
+};
+
+/**
+ * Aborts the request.
+ *
+ * @api public
+ */
+
+Request.prototype.abort = function(){
+  this.cleanup();
+};
+
+/**
+ * Aborts pending requests when unloading the window. This is needed to prevent
+ * memory leaks (e.g. when using IE) and to ensure that no spurious error is
+ * emitted.
+ */
+
+if (global.document) {
+  Request.requestsCount = 0;
+  Request.requests = {};
+  if (global.attachEvent) {
+    global.attachEvent('onunload', unloadHandler);
+  } else if (global.addEventListener) {
+    global.addEventListener('beforeunload', unloadHandler, false);
+  }
+}
+
+function unloadHandler() {
+  for (var i in Request.requests) {
+    if (Request.requests.hasOwnProperty(i)) {
+      Request.requests[i].abort();
+    }
+  }
+}
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./polling":18,"component-emitter":9,"component-inherit":21,"debug":22,"xmlhttprequest":20}],18:[function(_dereq_,module,exports){
+/**
+ * Module dependencies.
+ */
+
+var Transport = _dereq_('../transport');
+var parseqs = _dereq_('parseqs');
+var parser = _dereq_('engine.io-parser');
+var inherit = _dereq_('component-inherit');
+var debug = _dereq_('debug')('engine.io-client:polling');
+
+/**
+ * Module exports.
+ */
+
+module.exports = Polling;
+
+/**
+ * Is XHR2 supported?
+ */
+
+var hasXHR2 = (function() {
+  var XMLHttpRequest = _dereq_('xmlhttprequest');
+  var xhr = new XMLHttpRequest({ xdomain: false });
+  return null != xhr.responseType;
+})();
+
+/**
+ * Polling interface.
+ *
+ * @param {Object} opts
+ * @api private
+ */
+
+function Polling(opts){
+  var forceBase64 = (opts && opts.forceBase64);
+  if (!hasXHR2 || forceBase64) {
+    this.supportsBinary = false;
+  }
+  Transport.call(this, opts);
+}
+
+/**
+ * Inherits from Transport.
+ */
+
+inherit(Polling, Transport);
+
+/**
+ * Transport name.
+ */
+
+Polling.prototype.name = 'polling';
+
+/**
+ * Opens the socket (triggers polling). We write a PING message to determine
+ * when the transport is open.
+ *
+ * @api private
+ */
+
+Polling.prototype.doOpen = function(){
+  this.poll();
+};
+
+/**
+ * Pauses polling.
+ *
+ * @param {Function} callback upon buffers are flushed and transport is paused
+ * @api private
+ */
+
+Polling.prototype.pause = function(onPause){
+  var pending = 0;
+  var self = this;
+
+  this.readyState = 'pausing';
+
+  function pause(){
+    debug('paused');
+    self.readyState = 'paused';
+    onPause();
+  }
+
+  if (this.polling || !this.writable) {
+    var total = 0;
+
+    if (this.polling) {
+      debug('we are currently polling - waiting to pause');
+      total++;
+      this.once('pollComplete', function(){
+        debug('pre-pause polling complete');
+        --total || pause();
+      });
+    }
+
+    if (!this.writable) {
+      debug('we are currently writing - waiting to pause');
+      total++;
+      this.once('drain', function(){
+        debug('pre-pause writing complete');
+        --total || pause();
+      });
+    }
+  } else {
+    pause();
+  }
+};
+
+/**
+ * Starts polling cycle.
+ *
+ * @api public
+ */
+
+Polling.prototype.poll = function(){
+  debug('polling');
+  this.polling = true;
+  this.doPoll();
+  this.emit('poll');
+};
+
+/**
+ * Overloads onData to detect payloads.
+ *
+ * @api private
+ */
+
+Polling.prototype.onData = function(data){
+  var self = this;
+  debug('polling got data %s', data);
+  var callback = function(packet, index, total) {
+    // if its the first message we consider the transport open
+    if ('opening' == self.readyState) {
+      self.onOpen();
+    }
+
+    // if its a close packet, we close the ongoing requests
+    if ('close' == packet.type) {
+      self.onClose();
+      return false;
+    }
+
+    // otherwise bypass onData and handle the message
+    self.onPacket(packet);
+  };
+
+  // decode payload
+  parser.decodePayload(data, this.socket.binaryType, callback);
+
+  // if an event did not trigger closing
+  if ('closed' != this.readyState) {
+    // if we got data we're not polling
+    this.polling = false;
+    this.emit('pollComplete');
+
+    if ('open' == this.readyState) {
+      this.poll();
+    } else {
+      debug('ignoring poll - transport state "%s"', this.readyState);
+    }
+  }
+};
+
+/**
+ * For polling, send a close packet.
+ *
+ * @api private
+ */
+
+Polling.prototype.doClose = function(){
+  var self = this;
+
+  function close(){
+    debug('writing close packet');
+    self.write([{ type: 'close' }]);
+  }
+
+  if ('open' == this.readyState) {
+    debug('transport open - closing');
+    close();
+  } else {
+    // in case we're trying to close while
+    // handshaking is in progress (GH-164)
+    debug('transport not open - deferring close');
+    this.once('open', close);
+  }
+};
+
+/**
+ * Writes a packets payload.
+ *
+ * @param {Array} data packets
+ * @param {Function} drain callback
+ * @api private
+ */
+
+Polling.prototype.write = function(packets){
+  var self = this;
+  this.writable = false;
+  var callbackfn = function() {
+    self.writable = true;
+    self.emit('drain');
+  };
+
+  var self = this;
+  parser.encodePayload(packets, this.supportsBinary, function(data) {
+    self.doWrite(data, callbackfn);
+  });
+};
+
+/**
+ * Generates uri for connection.
+ *
+ * @api private
+ */
+
+Polling.prototype.uri = function(){
+  var query = this.query || {};
+  var schema = this.secure ? 'https' : 'http';
+  var port = '';
+
+  // cache busting is forced
+  if (false !== this.timestampRequests) {
+    query[this.timestampParam] = +new Date + '-' + Transport.timestamps++;
+  }
+
+  if (!this.supportsBinary && !query.sid) {
+    query.b64 = 1;
+  }
+
+  query = parseqs.encode(query);
+
+  // avoid port if default for schema
+  if (this.port && (('https' == schema && this.port != 443) ||
+     ('http' == schema && this.port != 80))) {
+    port = ':' + this.port;
+  }
+
+  // prepend ? to query
+  if (query.length) {
+    query = '?' + query;
+  }
+
+  return schema + '://' + this.hostname + port + this.path + query;
+};
+
+},{"../transport":14,"component-inherit":21,"debug":22,"engine.io-parser":25,"parseqs":35,"xmlhttprequest":20}],19:[function(_dereq_,module,exports){
+/**
+ * Module dependencies.
+ */
+
+var Transport = _dereq_('../transport');
+var parser = _dereq_('engine.io-parser');
+var parseqs = _dereq_('parseqs');
+var inherit = _dereq_('component-inherit');
+var debug = _dereq_('debug')('engine.io-client:websocket');
+
+/**
+ * `ws` exposes a WebSocket-compatible interface in
+ * Node, or the `WebSocket` or `MozWebSocket` globals
+ * in the browser.
+ */
+
+var WebSocket = _dereq_('ws');
+
+/**
+ * Module exports.
+ */
+
+module.exports = WS;
+
+/**
+ * WebSocket transport constructor.
+ *
+ * @api {Object} connection options
+ * @api public
+ */
+
+function WS(opts){
+  var forceBase64 = (opts && opts.forceBase64);
+  if (forceBase64) {
+    this.supportsBinary = false;
+  }
+  Transport.call(this, opts);
+}
+
+/**
+ * Inherits from Transport.
+ */
+
+inherit(WS, Transport);
+
+/**
+ * Transport name.
+ *
+ * @api public
+ */
+
+WS.prototype.name = 'websocket';
+
+/*
+ * WebSockets support binary
+ */
+
+WS.prototype.supportsBinary = true;
+
+/**
+ * Opens socket.
+ *
+ * @api private
+ */
+
+WS.prototype.doOpen = function(){
+  if (!this.check()) {
+    // let probe timeout
+    return;
+  }
+
+  var self = this;
+  var uri = this.uri();
+  var protocols = void(0);
+  var opts = { agent: this.agent };
+
+  // SSL options for Node.js client
+  opts.pfx = this.pfx;
+  opts.key = this.key;
+  opts.passphrase = this.passphrase;
+  opts.cert = this.cert;
+  opts.ca = this.ca;
+  opts.ciphers = this.ciphers;
+  opts.rejectUnauthorized = this.rejectUnauthorized;
+
+  this.ws = new WebSocket(uri, protocols, opts);
+
+  if (this.ws.binaryType === undefined) {
+    this.supportsBinary = false;
+  }
+
+  this.ws.binaryType = 'arraybuffer';
+  this.addEventListeners();
+};
+
+/**
+ * Adds event listeners to the socket
+ *
+ * @api private
+ */
+
+WS.prototype.addEventListeners = function(){
+  var self = this;
+
+  this.ws.onopen = function(){
+    self.onOpen();
+  };
+  this.ws.onclose = function(){
+    self.onClose();
+  };
+  this.ws.onmessage = function(ev){
+    self.onData(ev.data);
+  };
+  this.ws.onerror = function(e){
+    self.onError('websocket error', e);
+  };
+};
+
+/**
+ * Override `onData` to use a timer on iOS.
+ * See: https://gist.github.com/mloughran/2052006
+ *
+ * @api private
+ */
+
+if ('undefined' != typeof navigator
+  && /iPad|iPhone|iPod/i.test(navigator.userAgent)) {
+  WS.prototype.onData = function(data){
+    var self = this;
+    setTimeout(function(){
+      Transport.prototype.onData.call(self, data);
+    }, 0);
+  };
+}
+
+/**
+ * Writes data to socket.
+ *
+ * @param {Array} array of packets.
+ * @api private
+ */
+
+WS.prototype.write = function(packets){
+  var self = this;
+  this.writable = false;
+  // encodePacket efficient as it uses WS framing
+  // no need for encodePayload
+  for (var i = 0, l = packets.length; i < l; i++) {
+    parser.encodePacket(packets[i], this.supportsBinary, function(data) {
+      //Sometimes the websocket has already been closed but the browser didn't
+      //have a chance of informing us about it yet, in that case send will
+      //throw an error
+      try {
+        self.ws.send(data);
+      } catch (e){
+        debug('websocket closed before onclose event');
+      }
+    });
+  }
+
+  function ondrain() {
+    self.writable = true;
+    self.emit('drain');
+  }
+  // fake drain
+  // defer to next tick to allow Socket to clear writeBuffer
+  setTimeout(ondrain, 0);
+};
+
+/**
+ * Called upon close
+ *
+ * @api private
+ */
+
+WS.prototype.onClose = function(){
+  Transport.prototype.onClose.call(this);
+};
+
+/**
+ * Closes socket.
+ *
+ * @api private
+ */
+
+WS.prototype.doClose = function(){
+  if (typeof this.ws !== 'undefined') {
+    this.ws.close();
+  }
+};
+
+/**
+ * Generates uri for connection.
+ *
+ * @api private
+ */
+
+WS.prototype.uri = function(){
+  var query = this.query || {};
+  var schema = this.secure ? 'wss' : 'ws';
+  var port = '';
+
+  // avoid port if default for schema
+  if (this.port && (('wss' == schema && this.port != 443)
+    || ('ws' == schema && this.port != 80))) {
+    port = ':' + this.port;
+  }
+
+  // append timestamp to URI
+  if (this.timestampRequests) {
+    query[this.timestampParam] = +new Date;
+  }
+
+  // communicate binary support capabilities
+  if (!this.supportsBinary) {
+    query.b64 = 1;
+  }
+
+  query = parseqs.encode(query);
+
+  // prepend ? to query
+  if (query.length) {
+    query = '?' + query;
+  }
+
+  return schema + '://' + this.hostname + port + this.path + query;
+};
+
+/**
+ * Feature detection for WebSocket.
+ *
+ * @return {Boolean} whether this transport is available.
+ * @api public
+ */
+
+WS.prototype.check = function(){
+  return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name);
+};
+
+},{"../transport":14,"component-inherit":21,"debug":22,"engine.io-parser":25,"parseqs":35,"ws":37}],20:[function(_dereq_,module,exports){
+// browser shim for xmlhttprequest module
+var hasCORS = _dereq_('has-cors');
+
+module.exports = function(opts) {
+  var xdomain = opts.xdomain;
+
+  // scheme must be same when usign XDomainRequest
+  // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
+  var xscheme = opts.xscheme;
+
+  // XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default.
+  // https://github.com/Automattic/engine.io-client/pull/217
+  var enablesXDR = opts.enablesXDR;
+
+  // XMLHttpRequest can be disabled on IE
+  try {
+    if ('undefined' != typeof XMLHttpRequest && (!xdomain || hasCORS)) {
+      return new XMLHttpRequest();
+    }
+  } catch (e) { }
+
+  // Use XDomainRequest for IE8 if enablesXDR is true
+  // because loading bar keeps flashing when using jsonp-polling
+  // https://github.com/yujiosaka/socke.io-ie8-loading-example
+  try {
+    if ('undefined' != typeof XDomainRequest && !xscheme && enablesXDR) {
+      return new XDomainRequest();
+    }
+  } catch (e) { }
+
+  if (!xdomain) {
+    try {
+      return new ActiveXObject('Microsoft.XMLHTTP');
+    } catch(e) { }
+  }
+}
+
+},{"has-cors":40}],21:[function(_dereq_,module,exports){
+
+module.exports = function(a, b){
+  var fn = function(){};
+  fn.prototype = b.prototype;
+  a.prototype = new fn;
+  a.prototype.constructor = a;
+};
+},{}],22:[function(_dereq_,module,exports){
+
+/**
+ * This is the web browser implementation of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = _dereq_('./debug');
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+
+/**
+ * Colors.
+ */
+
+exports.colors = [
+  'lightseagreen',
+  'forestgreen',
+  'goldenrod',
+  'dodgerblue',
+  'darkorchid',
+  'crimson'
+];
+
+/**
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+function useColors() {
+  // is webkit? http://stackoverflow.com/a/16459606/376773
+  return ('WebkitAppearance' in document.documentElement.style) ||
+    // is firebug? http://stackoverflow.com/a/398120/376773
+    (window.console && (console.firebug || (console.exception && console.table))) ||
+    // is firefox >= v31?
+    // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+    (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
+}
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+exports.formatters.j = function(v) {
+  return JSON.stringify(v);
+};
+
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs() {
+  var args = arguments;
+  var useColors = this.useColors;
+
+  args[0] = (useColors ? '%c' : '')
+    + this.namespace
+    + (useColors ? ' %c' : ' ')
+    + args[0]
+    + (useColors ? '%c ' : ' ')
+    + '+' + exports.humanize(this.diff);
+
+  if (!useColors) return args;
+
+  var c = 'color: ' + this.color;
+  args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
+
+  // the final "%c" is somewhat tricky, because there could be other
+  // arguments passed either before or after the %c, so we need to
+  // figure out the correct index to insert the CSS into
+  var index = 0;
+  var lastC = 0;
+  args[0].replace(/%[a-z%]/g, function(match) {
+    if ('%' === match) return;
+    index++;
+    if ('%c' === match) {
+      // we only are interested in the *last* %c
+      // (the user may have provided their own)
+      lastC = index;
+    }
+  });
+
+  args.splice(lastC, 0, c);
+  return args;
+}
+
+/**
+ * Invokes `console.log()` when available.
+ * No-op when `console.log` is not a "function".
+ *
+ * @api public
+ */
+
+function log() {
+  // This hackery is required for IE8,
+  // where the `console.log` function doesn't have 'apply'
+  return 'object' == typeof console
+    && 'function' == typeof console.log
+    && Function.prototype.apply.call(console.log, console, arguments);
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+  try {
+    if (null == namespaces) {
+      localStorage.removeItem('debug');
+    } else {
+      localStorage.debug = namespaces;
+    }
+  } catch(e) {}
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+  var r;
+  try {
+    r = localStorage.debug;
+  } catch(e) {}
+  return r;
+}
+
+/**
+ * Enable namespaces listed in `localStorage.debug` initially.
+ */
+
+exports.enable(load());
+
+},{"./debug":23}],23:[function(_dereq_,module,exports){
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = debug;
+exports.coerce = coerce;
+exports.disable = disable;
+exports.enable = enable;
+exports.enabled = enabled;
+exports.humanize = _dereq_('ms');
+
+/**
+ * The currently active debug mode names, and names to skip.
+ */
+
+exports.names = [];
+exports.skips = [];
+
+/**
+ * Map of special "%n" handling functions, for the debug "format" argument.
+ *
+ * Valid key names are a single, lowercased letter, i.e. "n".
+ */
+
+exports.formatters = {};
+
+/**
+ * Previously assigned color.
+ */
+
+var prevColor = 0;
+
+/**
+ * Previous log timestamp.
+ */
+
+var prevTime;
+
+/**
+ * Select a color.
+ *
+ * @return {Number}
+ * @api private
+ */
+
+function selectColor() {
+  return exports.colors[prevColor++ % exports.colors.length];
+}
+
+/**
+ * Create a debugger with the given `namespace`.
+ *
+ * @param {String} namespace
+ * @return {Function}
+ * @api public
+ */
+
+function debug(namespace) {
+
+  // define the `disabled` version
+  function disabled() {
+  }
+  disabled.enabled = false;
+
+  // define the `enabled` version
+  function enabled() {
+
+    var self = enabled;
+
+    // set `diff` timestamp
+    var curr = +new Date();
+    var ms = curr - (prevTime || curr);
+    self.diff = ms;
+    self.prev = prevTime;
+    self.curr = curr;
+    prevTime = curr;
+
+    // add the `color` if not set
+    if (null == self.useColors) self.useColors = exports.useColors();
+    if (null == self.color && self.useColors) self.color = selectColor();
+
+    var args = Array.prototype.slice.call(arguments);
+
+    args[0] = exports.coerce(args[0]);
+
+    if ('string' !== typeof args[0]) {
+      // anything else let's inspect with %o
+      args = ['%o'].concat(args);
+    }
+
+    // apply any `formatters` transformations
+    var index = 0;
+    args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
+      // if we encounter an escaped % then don't increase the array index
+      if (match === '%') return match;
+      index++;
+      var formatter = exports.formatters[format];
+      if ('function' === typeof formatter) {
+        var val = args[index];
+        match = formatter.call(self, val);
+
+        // now we need to remove `args[index]` since it's inlined in the `format`
+        args.splice(index, 1);
+        index--;
+      }
+      return match;
+    });
+
+    if ('function' === typeof exports.formatArgs) {
+      args = exports.formatArgs.apply(self, args);
+    }
+    var logFn = enabled.log || exports.log || console.log.bind(console);
+    logFn.apply(self, args);
+  }
+  enabled.enabled = true;
+
+  var fn = exports.enabled(namespace) ? enabled : disabled;
+
+  fn.namespace = namespace;
+
+  return fn;
+}
+
+/**
+ * Enables a debug mode by namespaces. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} namespaces
+ * @api public
+ */
+
+function enable(namespaces) {
+  exports.save(namespaces);
+
+  var split = (namespaces || '').split(/[\s,]+/);
+  var len = split.length;
+
+  for (var i = 0; i < len; i++) {
+    if (!split[i]) continue; // ignore empty strings
+    namespaces = split[i].replace(/\*/g, '.*?');
+    if (namespaces[0] === '-') {
+      exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
+    } else {
+      exports.names.push(new RegExp('^' + namespaces + '$'));
+    }
+  }
+}
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+function disable() {
+  exports.enable('');
+}
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+function enabled(name) {
+  var i, len;
+  for (i = 0, len = exports.skips.length; i < len; i++) {
+    if (exports.skips[i].test(name)) {
+      return false;
+    }
+  }
+  for (i = 0, len = exports.names.length; i < len; i++) {
+    if (exports.names[i].test(name)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Coerce `val`.
+ *
+ * @param {Mixed} val
+ * @return {Mixed}
+ * @api private
+ */
+
+function coerce(val) {
+  if (val instanceof Error) return val.stack || val.message;
+  return val;
+}
+
+},{"ms":24}],24:[function(_dereq_,module,exports){
+/**
+ * Helpers.
+ */
+
+var s = 1000;
+var m = s * 60;
+var h = m * 60;
+var d = h * 24;
+var y = d * 365.25;
+
+/**
+ * Parse or format the given `val`.
+ *
+ * Options:
+ *
+ *  - `long` verbose formatting [false]
+ *
+ * @param {String|Number} val
+ * @param {Object} options
+ * @return {String|Number}
+ * @api public
+ */
+
+module.exports = function(val, options){
+  options = options || {};
+  if ('string' == typeof val) return parse(val);
+  return options.long
+    ? long(val)
+    : short(val);
+};
+
+/**
+ * Parse the given `str` and return milliseconds.
+ *
+ * @param {String} str
+ * @return {Number}
+ * @api private
+ */
+
+function parse(str) {
+  var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
+  if (!match) return;
+  var n = parseFloat(match[1]);
+  var type = (match[2] || 'ms').toLowerCase();
+  switch (type) {
+    case 'years':
+    case 'year':
+    case 'y':
+      return n * y;
+    case 'days':
+    case 'day':
+    case 'd':
+      return n * d;
+    case 'hours':
+    case 'hour':
+    case 'h':
+      return n * h;
+    case 'minutes':
+    case 'minute':
+    case 'm':
+      return n * m;
+    case 'seconds':
+    case 'second':
+    case 's':
+      return n * s;
+    case 'ms':
+      return n;
+  }
+}
+
+/**
+ * Short format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
+
+function short(ms) {
+  if (ms >= d) return Math.round(ms / d) + 'd';
+  if (ms >= h) return Math.round(ms / h) + 'h';
+  if (ms >= m) return Math.round(ms / m) + 'm';
+  if (ms >= s) return Math.round(ms / s) + 's';
+  return ms + 'ms';
+}
+
+/**
+ * Long format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
+
+function long(ms) {
+  return plural(ms, d, 'day')
+    || plural(ms, h, 'hour')
+    || plural(ms, m, 'minute')
+    || plural(ms, s, 'second')
+    || ms + ' ms';
+}
+
+/**
+ * Pluralization helper.
+ */
+
+function plural(ms, n, name) {
+  if (ms < n) return;
+  if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
+  return Math.ceil(ms / n) + ' ' + name + 's';
+}
+
+},{}],25:[function(_dereq_,module,exports){
+(function (global){
+/**
+ * Module dependencies.
+ */
+
+var keys = _dereq_('./keys');
+var hasBinary = _dereq_('has-binary');
+var sliceBuffer = _dereq_('arraybuffer.slice');
+var base64encoder = _dereq_('base64-arraybuffer');
+var after = _dereq_('after');
+var utf8 = _dereq_('utf8');
+
+/**
+ * Check if we are running an android browser. That requires us to use
+ * ArrayBuffer with polling transports...
+ *
+ * http://ghinda.net/jpeg-blob-ajax-android/
+ */
+
+var isAndroid = navigator.userAgent.match(/Android/i);
+
+/**
+ * Check if we are running in PhantomJS.
+ * Uploading a Blob with PhantomJS does not work correctly, as reported here:
+ * https://github.com/ariya/phantomjs/issues/11395
+ * @type boolean
+ */
+var isPhantomJS = /PhantomJS/i.test(navigator.userAgent);
+
+/**
+ * When true, avoids using Blobs to encode payloads.
+ * @type boolean
+ */
+var dontSendBlobs = isAndroid || isPhantomJS;
+
+/**
+ * Current protocol version.
+ */
+
+exports.protocol = 3;
+
+/**
+ * Packet types.
+ */
+
+var packets = exports.packets = {
+    open:     0    // non-ws
+  , close:    1    // non-ws
+  , ping:     2
+  , pong:     3
+  , message:  4
+  , upgrade:  5
+  , noop:     6
+};
+
+var packetslist = keys(packets);
+
+/**
+ * Premade error packet.
+ */
+
+var err = { type: 'error', data: 'parser error' };
+
+/**
+ * Create a blob api even for blob builder when vendor prefixes exist
+ */
+
+var Blob = _dereq_('blob');
+
+/**
+ * Encodes a packet.
+ *
+ *     <packet type id> [ <data> ]
+ *
+ * Example:
+ *
+ *     5hello world
+ *     3
+ *     4
+ *
+ * Binary is encoded in an identical principle
+ *
+ * @api private
+ */
+
+exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
+  if ('function' == typeof supportsBinary) {
+    callback = supportsBinary;
+    supportsBinary = false;
+  }
+
+  if ('function' == typeof utf8encode) {
+    callback = utf8encode;
+    utf8encode = null;
+  }
+
+  var data = (packet.data === undefined)
+    ? undefined
+    : packet.data.buffer || packet.data;
+
+  if (global.ArrayBuffer && data instanceof ArrayBuffer) {
+    return encodeArrayBuffer(packet, supportsBinary, callback);
+  } else if (Blob && data instanceof global.Blob) {
+    return encodeBlob(packet, supportsBinary, callback);
+  }
+
+  // might be an object with { base64: true, data: dataAsBase64String }
+  if (data && data.base64) {
+    return encodeBase64Object(packet, callback);
+  }
+
+  // Sending data as a utf-8 string
+  var encoded = packets[packet.type];
+
+  // data fragment is optional
+  if (undefined !== packet.data) {
+    encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
+  }
+
+  return callback('' + encoded);
+
+};
+
+function encodeBase64Object(packet, callback) {
+  // packet data is an object { base64: true, data: dataAsBase64String }
+  var message = 'b' + exports.packets[packet.type] + packet.data.data;
+  return callback(message);
+}
+
+/**
+ * Encode packet helpers for binary types
+ */
+
+function encodeArrayBuffer(packet, supportsBinary, callback) {
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  var data = packet.data;
+  var contentArray = new Uint8Array(data);
+  var resultBuffer = new Uint8Array(1 + data.byteLength);
+
+  resultBuffer[0] = packets[packet.type];
+  for (var i = 0; i < contentArray.length; i++) {
+    resultBuffer[i+1] = contentArray[i];
+  }
+
+  return callback(resultBuffer.buffer);
+}
+
+function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  var fr = new FileReader();
+  fr.onload = function() {
+    packet.data = fr.result;
+    exports.encodePacket(packet, supportsBinary, true, callback);
+  };
+  return fr.readAsArrayBuffer(packet.data);
+}
+
+function encodeBlob(packet, supportsBinary, callback) {
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  if (dontSendBlobs) {
+    return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
+  }
+
+  var length = new Uint8Array(1);
+  length[0] = packets[packet.type];
+  var blob = new Blob([length.buffer, packet.data]);
+
+  return callback(blob);
+}
+
+/**
+ * Encodes a packet with binary data in a base64 string
+ *
+ * @param {Object} packet, has `type` and `data`
+ * @return {String} base64 encoded message
+ */
+
+exports.encodeBase64Packet = function(packet, callback) {
+  var message = 'b' + exports.packets[packet.type];
+  if (Blob && packet.data instanceof Blob) {
+    var fr = new FileReader();
+    fr.onload = function() {
+      var b64 = fr.result.split(',')[1];
+      callback(message + b64);
+    };
+    return fr.readAsDataURL(packet.data);
+  }
+
+  var b64data;
+  try {
+    b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
+  } catch (e) {
+    // iPhone Safari doesn't let you apply with typed arrays
+    var typed = new Uint8Array(packet.data);
+    var basic = new Array(typed.length);
+    for (var i = 0; i < typed.length; i++) {
+      basic[i] = typed[i];
+    }
+    b64data = String.fromCharCode.apply(null, basic);
+  }
+  message += global.btoa(b64data);
+  return callback(message);
+};
+
+/**
+ * Decodes a packet. Changes format to Blob if requested.
+ *
+ * @return {Object} with `type` and `data` (if any)
+ * @api private
+ */
+
+exports.decodePacket = function (data, binaryType, utf8decode) {
+  // String data
+  if (typeof data == 'string' || data === undefined) {
+    if (data.charAt(0) == 'b') {
+      return exports.decodeBase64Packet(data.substr(1), binaryType);
+    }
+
+    if (utf8decode) {
+      try {
+        data = utf8.decode(data);
+      } catch (e) {
+        return err;
+      }
+    }
+    var type = data.charAt(0);
+
+    if (Number(type) != type || !packetslist[type]) {
+      return err;
+    }
+
+    if (data.length > 1) {
+      return { type: packetslist[type], data: data.substring(1) };
+    } else {
+      return { type: packetslist[type] };
+    }
+  }
+
+  var asArray = new Uint8Array(data);
+  var type = asArray[0];
+  var rest = sliceBuffer(data, 1);
+  if (Blob && binaryType === 'blob') {
+    rest = new Blob([rest]);
+  }
+  return { type: packetslist[type], data: rest };
+};
+
+/**
+ * Decodes a packet encoded in a base64 string
+ *
+ * @param {String} base64 encoded message
+ * @return {Object} with `type` and `data` (if any)
+ */
+
+exports.decodeBase64Packet = function(msg, binaryType) {
+  var type = packetslist[msg.charAt(0)];
+  if (!global.ArrayBuffer) {
+    return { type: type, data: { base64: true, data: msg.substr(1) } };
+  }
+
+  var data = base64encoder.decode(msg.substr(1));
+
+  if (binaryType === 'blob' && Blob) {
+    data = new Blob([data]);
+  }
+
+  return { type: type, data: data };
+};
+
+/**
+ * Encodes multiple messages (payload).
+ *
+ *     <length>:data
+ *
+ * Example:
+ *
+ *     11:hello world2:hi
+ *
+ * If any contents are binary, they will be encoded as base64 strings. Base64
+ * encoded strings are marked with a b before the length specifier
+ *
+ * @param {Array} packets
+ * @api private
+ */
+
+exports.encodePayload = function (packets, supportsBinary, callback) {
+  if (typeof supportsBinary == 'function') {
+    callback = supportsBinary;
+    supportsBinary = null;
+  }
+
+  var isBinary = hasBinary(packets);
+
+  if (supportsBinary && isBinary) {
+    if (Blob && !dontSendBlobs) {
+      return exports.encodePayloadAsBlob(packets, callback);
+    }
+
+    return exports.encodePayloadAsArrayBuffer(packets, callback);
+  }
+
+  if (!packets.length) {
+    return callback('0:');
+  }
+
+  function setLengthHeader(message) {
+    return message.length + ':' + message;
+  }
+
+  function encodeOne(packet, doneCallback) {
+    exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
+      doneCallback(null, setLengthHeader(message));
+    });
+  }
+
+  map(packets, encodeOne, function(err, results) {
+    return callback(results.join(''));
+  });
+};
+
+/**
+ * Async array map using after
+ */
+
+function map(ary, each, done) {
+  var result = new Array(ary.length);
+  var next = after(ary.length, done);
+
+  var eachWithIndex = function(i, el, cb) {
+    each(el, function(error, msg) {
+      result[i] = msg;
+      cb(error, result);
+    });
+  };
+
+  for (var i = 0; i < ary.length; i++) {
+    eachWithIndex(i, ary[i], next);
+  }
+}
+
+/*
+ * Decodes data when a payload is maybe expected. Possible binary contents are
+ * decoded from their base64 representation
+ *
+ * @param {String} data, callback method
+ * @api public
+ */
+
+exports.decodePayload = function (data, binaryType, callback) {
+  if (typeof data != 'string') {
+    return exports.decodePayloadAsBinary(data, binaryType, callback);
+  }
+
+  if (typeof binaryType === 'function') {
+    callback = binaryType;
+    binaryType = null;
+  }
+
+  var packet;
+  if (data == '') {
+    // parser error - ignoring payload
+    return callback(err, 0, 1);
+  }
+
+  var length = ''
+    , n, msg;
+
+  for (var i = 0, l = data.length; i < l; i++) {
+    var chr = data.charAt(i);
+
+    if (':' != chr) {
+      length += chr;
+    } else {
+      if ('' == length || (length != (n = Number(length)))) {
+        // parser error - ignoring payload
+        return callback(err, 0, 1);
+      }
+
+      msg = data.substr(i + 1, n);
+
+      if (length != msg.length) {
+        // parser error - ignoring payload
+        return callback(err, 0, 1);
+      }
+
+      if (msg.length) {
+        packet = exports.decodePacket(msg, binaryType, true);
+
+        if (err.type == packet.type && err.data == packet.data) {
+          // parser error in individual packet - ignoring payload
+          return callback(err, 0, 1);
+        }
+
+        var ret = callback(packet, i + n, l);
+        if (false === ret) return;
+      }
+
+      // advance cursor
+      i += n;
+      length = '';
+    }
+  }
+
+  if (length != '') {
+    // parser error - ignoring payload
+    return callback(err, 0, 1);
+  }
+
+};
+
+/**
+ * Encodes multiple messages (payload) as binary.
+ *
+ * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
+ * 255><data>
+ *
+ * Example:
+ * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
+ *
+ * @param {Array} packets
+ * @return {ArrayBuffer} encoded payload
+ * @api private
+ */
+
+exports.encodePayloadAsArrayBuffer = function(packets, callback) {
+  if (!packets.length) {
+    return callback(new ArrayBuffer(0));
+  }
+
+  function encodeOne(packet, doneCallback) {
+    exports.encodePacket(packet, true, true, function(data) {
+      return doneCallback(null, data);
+    });
+  }
+
+  map(packets, encodeOne, function(err, encodedPackets) {
+    var totalLength = encodedPackets.reduce(function(acc, p) {
+      var len;
+      if (typeof p === 'string'){
+        len = p.length;
+      } else {
+        len = p.byteLength;
+      }
+      return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
+    }, 0);
+
+    var resultArray = new Uint8Array(totalLength);
+
+    var bufferIndex = 0;
+    encodedPackets.forEach(function(p) {
+      var isString = typeof p === 'string';
+      var ab = p;
+      if (isString) {
+        var view = new Uint8Array(p.length);
+        for (var i = 0; i < p.length; i++) {
+          view[i] = p.charCodeAt(i);
+        }
+        ab = view.buffer;
+      }
+
+      if (isString) { // not true binary
+        resultArray[bufferIndex++] = 0;
+      } else { // true binary
+        resultArray[bufferIndex++] = 1;
+      }
+
+      var lenStr = ab.byteLength.toString();
+      for (var i = 0; i < lenStr.length; i++) {
+        resultArray[bufferIndex++] = parseInt(lenStr[i]);
+      }
+      resultArray[bufferIndex++] = 255;
+
+      var view = new Uint8Array(ab);
+      for (var i = 0; i < view.length; i++) {
+        resultArray[bufferIndex++] = view[i];
+      }
+    });
+
+    return callback(resultArray.buffer);
+  });
+};
+
+/**
+ * Encode as Blob
+ */
+
+exports.encodePayloadAsBlob = function(packets, callback) {
+  function encodeOne(packet, doneCallback) {
+    exports.encodePacket(packet, true, true, function(encoded) {
+      var binaryIdentifier = new Uint8Array(1);
+      binaryIdentifier[0] = 1;
+      if (typeof encoded === 'string') {
+        var view = new Uint8Array(encoded.length);
+        for (var i = 0; i < encoded.length; i++) {
+          view[i] = encoded.charCodeAt(i);
+        }
+        encoded = view.buffer;
+        binaryIdentifier[0] = 0;
+      }
+
+      var len = (encoded instanceof ArrayBuffer)
+        ? encoded.byteLength
+        : encoded.size;
+
+      var lenStr = len.toString();
+      var lengthAry = new Uint8Array(lenStr.length + 1);
+      for (var i = 0; i < lenStr.length; i++) {
+        lengthAry[i] = parseInt(lenStr[i]);
+      }
+      lengthAry[lenStr.length] = 255;
+
+      if (Blob) {
+        var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
+        doneCallback(null, blob);
+      }
+    });
+  }
+
+  map(packets, encodeOne, function(err, results) {
+    return callback(new Blob(results));
+  });
+};
+
+/*
+ * Decodes data when a payload is maybe expected. Strings are decoded by
+ * interpreting each byte as a key code for entries marked to start with 0. See
+ * description of encodePayloadAsBinary
+ *
+ * @param {ArrayBuffer} data, callback method
+ * @api public
+ */
+
+exports.decodePayloadAsBinary = function (data, binaryType, callback) {
+  if (typeof binaryType === 'function') {
+    callback = binaryType;
+    binaryType = null;
+  }
+
+  var bufferTail = data;
+  var buffers = [];
+
+  var numberTooLong = false;
+  while (bufferTail.byteLength > 0) {
+    var tailArray = new Uint8Array(bufferTail);
+    var isString = tailArray[0] === 0;
+    var msgLength = '';
+
+    for (var i = 1; ; i++) {
+      if (tailArray[i] == 255) break;
+
+      if (msgLength.length > 310) {
+        numberTooLong = true;
+        break;
+      }
+
+      msgLength += tailArray[i];
+    }
+
+    if(numberTooLong) return callback(err, 0, 1);
+
+    bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
+    msgLength = parseInt(msgLength);
+
+    var msg = sliceBuffer(bufferTail, 0, msgLength);
+    if (isString) {
+      try {
+        msg = String.fromCharCode.apply(null, new Uint8Array(msg));
+      } catch (e) {
+        // iPhone Safari doesn't let you apply to typed arrays
+        var typed = new Uint8Array(msg);
+        msg = '';
+        for (var i = 0; i < typed.length; i++) {
+          msg += String.fromCharCode(typed[i]);
+        }
+      }
+    }
+
+    buffers.push(msg);
+    bufferTail = sliceBuffer(bufferTail, msgLength);
+  }
+
+  var total = buffers.length;
+  buffers.forEach(function(buffer, i) {
+    callback(exports.decodePacket(buffer, binaryType, true), i, total);
+  });
+};
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./keys":26,"after":27,"arraybuffer.slice":28,"base64-arraybuffer":29,"blob":30,"has-binary":31,"utf8":33}],26:[function(_dereq_,module,exports){
+
+/**
+ * Gets the keys for an object.
+ *
+ * @return {Array} keys
+ * @api private
+ */
+
+module.exports = Object.keys || function keys (obj){
+  var arr = [];
+  var has = Object.prototype.hasOwnProperty;
+
+  for (var i in obj) {
+    if (has.call(obj, i)) {
+      arr.push(i);
+    }
+  }
+  return arr;
+};
+
+},{}],27:[function(_dereq_,module,exports){
+module.exports = after
+
+function after(count, callback, err_cb) {
+    var bail = false
+    err_cb = err_cb || noop
+    proxy.count = count
+
+    return (count === 0) ? callback() : proxy
+
+    function proxy(err, result) {
+        if (proxy.count <= 0) {
+            throw new Error('after called too many times')
+        }
+        --proxy.count
+
+        // after first error, rest are passed to err_cb
+        if (err) {
+            bail = true
+            callback(err)
+            // future error callbacks will go to error handler
+            callback = err_cb
+        } else if (proxy.count === 0 && !bail) {
+            callback(null, result)
+        }
+    }
+}
+
+function noop() {}
+
+},{}],28:[function(_dereq_,module,exports){
+/**
+ * An abstraction for slicing an arraybuffer even when
+ * ArrayBuffer.prototype.slice is not supported
+ *
+ * @api public
+ */
+
+module.exports = function(arraybuffer, start, end) {
+  var bytes = arraybuffer.byteLength;
+  start = start || 0;
+  end = end || bytes;
+
+  if (arraybuffer.slice) { return arraybuffer.slice(start, end); }
+
+  if (start < 0) { start += bytes; }
+  if (end < 0) { end += bytes; }
+  if (end > bytes) { end = bytes; }
+
+  if (start >= bytes || start >= end || bytes === 0) {
+    return new ArrayBuffer(0);
+  }
+
+  var abv = new Uint8Array(arraybuffer);
+  var result = new Uint8Array(end - start);
+  for (var i = start, ii = 0; i < end; i++, ii++) {
+    result[ii] = abv[i];
+  }
+  return result.buffer;
+};
+
+},{}],29:[function(_dereq_,module,exports){
+/*
+ * base64-arraybuffer
+ * https://github.com/niklasvh/base64-arraybuffer
+ *
+ * Copyright (c) 2012 Niklas von Hertzen
+ * Licensed under the MIT license.
+ */
+(function(chars){
+  "use strict";
+
+  exports.encode = function(arraybuffer) {
+    var bytes = new Uint8Array(arraybuffer),
+    i, len = bytes.length, base64 = "";
+
+    for (i = 0; i < len; i+=3) {
+      base64 += chars[bytes[i] >> 2];
+      base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
+      base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
+      base64 += chars[bytes[i + 2] & 63];
+    }
+
+    if ((len % 3) === 2) {
+      base64 = base64.substring(0, base64.length - 1) + "=";
+    } else if (len % 3 === 1) {
+      base64 = base64.substring(0, base64.length - 2) + "==";
+    }
+
+    return base64;
+  };
+
+  exports.decode =  function(base64) {
+    var bufferLength = base64.length * 0.75,
+    len = base64.length, i, p = 0,
+    encoded1, encoded2, encoded3, encoded4;
+
+    if (base64[base64.length - 1] === "=") {
+      bufferLength--;
+      if (base64[base64.length - 2] === "=") {
+        bufferLength--;
+      }
+    }
+
+    var arraybuffer = new ArrayBuffer(bufferLength),
+    bytes = new Uint8Array(arraybuffer);
+
+    for (i = 0; i < len; i+=4) {
+      encoded1 = chars.indexOf(base64[i]);
+      encoded2 = chars.indexOf(base64[i+1]);
+      encoded3 = chars.indexOf(base64[i+2]);
+      encoded4 = chars.indexOf(base64[i+3]);
+
+      bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
+      bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
+      bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
+    }
+
+    return arraybuffer;
+  };
+})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+
+},{}],30:[function(_dereq_,module,exports){
+(function (global){
+/**
+ * Create a blob builder even when vendor prefixes exist
+ */
+
+var BlobBuilder = global.BlobBuilder
+  || global.WebKitBlobBuilder
+  || global.MSBlobBuilder
+  || global.MozBlobBuilder;
+
+/**
+ * Check if Blob constructor is supported
+ */
+
+var blobSupported = (function() {
+  try {
+    var b = new Blob(['hi']);
+    return b.size == 2;
+  } catch(e) {
+    return false;
+  }
+})();
+
+/**
+ * Check if BlobBuilder is supported
+ */
+
+var blobBuilderSupported = BlobBuilder
+  && BlobBuilder.prototype.append
+  && BlobBuilder.prototype.getBlob;
+
+function BlobBuilderConstructor(ary, options) {
+  options = options || {};
+
+  var bb = new BlobBuilder();
+  for (var i = 0; i < ary.length; i++) {
+    bb.append(ary[i]);
+  }
+  return (options.type) ? bb.getBlob(options.type) : bb.getBlob();
+};
+
+module.exports = (function() {
+  if (blobSupported) {
+    return global.Blob;
+  } else if (blobBuilderSupported) {
+    return BlobBuilderConstructor;
+  } else {
+    return undefined;
+  }
+})();
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],31:[function(_dereq_,module,exports){
+(function (global){
+
+/*
+ * Module requirements.
+ */
+
+var isArray = _dereq_('isarray');
+
+/**
+ * Module exports.
+ */
+
+module.exports = hasBinary;
+
+/**
+ * Checks for binary data.
+ *
+ * Right now only Buffer and ArrayBuffer are supported..
+ *
+ * @param {Object} anything
+ * @api public
+ */
+
+function hasBinary(data) {
+
+  function _hasBinary(obj) {
+    if (!obj) return false;
+
+    if ( (global.Buffer && global.Buffer.isBuffer(obj)) ||
+         (global.ArrayBuffer && obj instanceof ArrayBuffer) ||
+         (global.Blob && obj instanceof Blob) ||
+         (global.File && obj instanceof File)
+        ) {
+      return true;
+    }
+
+    if (isArray(obj)) {
+      for (var i = 0; i < obj.length; i++) {
+          if (_hasBinary(obj[i])) {
+              return true;
+          }
+      }
+    } else if (obj && 'object' == typeof obj) {
+      if (obj.toJSON) {
+        obj = obj.toJSON();
+      }
+
+      for (var key in obj) {
+        if (obj.hasOwnProperty(key) && _hasBinary(obj[key])) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  return _hasBinary(data);
+}
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"isarray":32}],32:[function(_dereq_,module,exports){
+module.exports = Array.isArray || function (arr) {
+  return Object.prototype.toString.call(arr) == '[object Array]';
+};
+
+},{}],33:[function(_dereq_,module,exports){
+(function (global){
+/*! http://mths.be/utf8js v2.0.0 by @mathias */
+;(function(root) {
+
+	// Detect free variables `exports`
+	var freeExports = typeof exports == 'object' && exports;
+
+	// Detect free variable `module`
+	var freeModule = typeof module == 'object' && module &&
+		module.exports == freeExports && module;
+
+	// Detect free variable `global`, from Node.js or Browserified code,
+	// and use it as `root`
+	var freeGlobal = typeof global == 'object' && global;
+	if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
+		root = freeGlobal;
+	}
+
+	/*--------------------------------------------------------------------------*/
+
+	var stringFromCharCode = String.fromCharCode;
+
+	// Taken from http://mths.be/punycode
+	function ucs2decode(string) {
+		var output = [];
+		var counter = 0;
+		var length = string.length;
+		var value;
+		var extra;
+		while (counter < length) {
+			value = string.charCodeAt(counter++);
+			if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+				// high surrogate, and there is a next character
+				extra = string.charCodeAt(counter++);
+				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+				} else {
+					// unmatched surrogate; only append this code unit, in case the next
+					// code unit is the high surrogate of a surrogate pair
+					output.push(value);
+					counter--;
+				}
+			} else {
+				output.push(value);
+			}
+		}
+		return output;
+	}
+
+	// Taken from http://mths.be/punycode
+	function ucs2encode(array) {
+		var length = array.length;
+		var index = -1;
+		var value;
+		var output = '';
+		while (++index < length) {
+			value = array[index];
+			if (value > 0xFFFF) {
+				value -= 0x10000;
+				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+				value = 0xDC00 | value & 0x3FF;
+			}
+			output += stringFromCharCode(value);
+		}
+		return output;
+	}
+
+	/*--------------------------------------------------------------------------*/
+
+	function createByte(codePoint, shift) {
+		return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80);
+	}
+
+	function encodeCodePoint(codePoint) {
+		if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence
+			return stringFromCharCode(codePoint);
+		}
+		var symbol = '';
+		if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence
+			symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0);
+		}
+		else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence
+			symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0);
+			symbol += createByte(codePoint, 6);
+		}
+		else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence
+			symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0);
+			symbol += createByte(codePoint, 12);
+			symbol += createByte(codePoint, 6);
+		}
+		symbol += stringFromCharCode((codePoint & 0x3F) | 0x80);
+		return symbol;
+	}
+
+	function utf8encode(string) {
+		var codePoints = ucs2decode(string);
+
+		// console.log(JSON.stringify(codePoints.map(function(x) {
+		// 	return 'U+' + x.toString(16).toUpperCase();
+		// })));
+
+		var length = codePoints.length;
+		var index = -1;
+		var codePoint;
+		var byteString = '';
+		while (++index < length) {
+			codePoint = codePoints[index];
+			byteString += encodeCodePoint(codePoint);
+		}
+		return byteString;
+	}
+
+	/*--------------------------------------------------------------------------*/
+
+	function readContinuationByte() {
+		if (byteIndex >= byteCount) {
+			throw Error('Invalid byte index');
+		}
+
+		var continuationByte = byteArray[byteIndex] & 0xFF;
+		byteIndex++;
+
+		if ((continuationByte & 0xC0) == 0x80) {
+			return continuationByte & 0x3F;
+		}
+
+		// If we end up here, it’s not a continuation byte
+		throw Error('Invalid continuation byte');
+	}
+
+	function decodeSymbol() {
+		var byte1;
+		var byte2;
+		var byte3;
+		var byte4;
+		var codePoint;
+
+		if (byteIndex > byteCount) {
+			throw Error('Invalid byte index');
+		}
+
+		if (byteIndex == byteCount) {
+			return false;
+		}
+
+		// Read first byte
+		byte1 = byteArray[byteIndex] & 0xFF;
+		byteIndex++;
+
+		// 1-byte sequence (no continuation bytes)
+		if ((byte1 & 0x80) == 0) {
+			return byte1;
+		}
+
+		// 2-byte sequence
+		if ((byte1 & 0xE0) == 0xC0) {
+			var byte2 = readContinuationByte();
+			codePoint = ((byte1 & 0x1F) << 6) | byte2;
+			if (codePoint >= 0x80) {
+				return codePoint;
+			} else {
+				throw Error('Invalid continuation byte');
+			}
+		}
+
+		// 3-byte sequence (may include unpaired surrogates)
+		if ((byte1 & 0xF0) == 0xE0) {
+			byte2 = readContinuationByte();
+			byte3 = readContinuationByte();
+			codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
+			if (codePoint >= 0x0800) {
+				return codePoint;
+			} else {
+				throw Error('Invalid continuation byte');
+			}
+		}
+
+		// 4-byte sequence
+		if ((byte1 & 0xF8) == 0xF0) {
+			byte2 = readContinuationByte();
+			byte3 = readContinuationByte();
+			byte4 = readContinuationByte();
+			codePoint = ((byte1 & 0x0F) << 0x12) | (byte2 << 0x0C) |
+				(byte3 << 0x06) | byte4;
+			if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) {
+				return codePoint;
+			}
+		}
+
+		throw Error('Invalid UTF-8 detected');
+	}
+
+	var byteArray;
+	var byteCount;
+	var byteIndex;
+	function utf8decode(byteString) {
+		byteArray = ucs2decode(byteString);
+		byteCount = byteArray.length;
+		byteIndex = 0;
+		var codePoints = [];
+		var tmp;
+		while ((tmp = decodeSymbol()) !== false) {
+			codePoints.push(tmp);
+		}
+		return ucs2encode(codePoints);
+	}
+
+	/*--------------------------------------------------------------------------*/
+
+	var utf8 = {
+		'version': '2.0.0',
+		'encode': utf8encode,
+		'decode': utf8decode
+	};
+
+	// Some AMD build optimizers, like r.js, check for specific condition patterns
+	// like the following:
+	if (
+		typeof define == 'function' &&
+		typeof define.amd == 'object' &&
+		define.amd
+	) {
+		define(function() {
+			return utf8;
+		});
+	}	else if (freeExports && !freeExports.nodeType) {
+		if (freeModule) { // in Node.js or RingoJS v0.8.0+
+			freeModule.exports = utf8;
+		} else { // in Narwhal or RingoJS v0.7.0-
+			var object = {};
+			var hasOwnProperty = object.hasOwnProperty;
+			for (var key in utf8) {
+				hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]);
+			}
+		}
+	} else { // in Rhino or a web browser
+		root.utf8 = utf8;
+	}
+
+}(this));
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],34:[function(_dereq_,module,exports){
+(function (global){
+/**
+ * JSON parse.
+ *
+ * @see Based on jQuery#parseJSON (MIT) and JSON2
+ * @api private
+ */
+
+var rvalidchars = /^[\],:{}\s]*$/;
+var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
+var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
+var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g;
+var rtrimLeft = /^\s+/;
+var rtrimRight = /\s+$/;
+
+module.exports = function parsejson(data) {
+  if ('string' != typeof data || !data) {
+    return null;
+  }
+
+  data = data.replace(rtrimLeft, '').replace(rtrimRight, '');
+
+  // Attempt to parse using the native JSON parser first
+  if (global.JSON && JSON.parse) {
+    return JSON.parse(data);
+  }
+
+  if (rvalidchars.test(data.replace(rvalidescape, '@')
+      .replace(rvalidtokens, ']')
+      .replace(rvalidbraces, ''))) {
+    return (new Function('return ' + data))();
+  }
+};
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],35:[function(_dereq_,module,exports){
+/**
+ * Compiles a querystring
+ * Returns string representation of the object
+ *
+ * @param {Object}
+ * @api private
+ */
+
+exports.encode = function (obj) {
+  var str = '';
+
+  for (var i in obj) {
+    if (obj.hasOwnProperty(i)) {
+      if (str.length) str += '&';
+      str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
+    }
+  }
+
+  return str;
+};
+
+/**
+ * Parses a simple querystring into an object
+ *
+ * @param {String} qs
+ * @api private
+ */
+
+exports.decode = function(qs){
+  var qry = {};
+  var pairs = qs.split('&');
+  for (var i = 0, l = pairs.length; i < l; i++) {
+    var pair = pairs[i].split('=');
+    qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
+  }
+  return qry;
+};
+
+},{}],36:[function(_dereq_,module,exports){
+/**
+ * Parses an URI
+ *
+ * @author Steven Levithan <stevenlevithan.com> (MIT license)
+ * @api private
+ */
+
+var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
+
+var parts = [
+    'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
+];
+
+module.exports = function parseuri(str) {
+    var src = str,
+        b = str.indexOf('['),
+        e = str.indexOf(']');
+
+    if (b != -1 && e != -1) {
+        str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);
+    }
+
+    var m = re.exec(str || ''),
+        uri = {},
+        i = 14;
+
+    while (i--) {
+        uri[parts[i]] = m[i] || '';
+    }
+
+    if (b != -1 && e != -1) {
+        uri.source = src;
+        uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');
+        uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');
+        uri.ipv6uri = true;
+    }
+
+    return uri;
+};
+
+},{}],37:[function(_dereq_,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var global = (function() { return this; })();
+
+/**
+ * WebSocket constructor.
+ */
+
+var WebSocket = global.WebSocket || global.MozWebSocket;
+
+/**
+ * Module exports.
+ */
+
+module.exports = WebSocket ? ws : null;
+
+/**
+ * WebSocket constructor.
+ *
+ * The third `opts` options object gets ignored in web browsers, since it's
+ * non-standard, and throws a TypeError if passed to the constructor.
+ * See: https://github.com/einaros/ws/issues/227
+ *
+ * @param {String} uri
+ * @param {Array} protocols (optional)
+ * @param {Object) opts (optional)
+ * @api public
+ */
+
+function ws(uri, protocols, opts) {
+  var instance;
+  if (protocols) {
+    instance = new WebSocket(uri, protocols);
+  } else {
+    instance = new WebSocket(uri);
+  }
+  return instance;
+}
+
+if (WebSocket) ws.prototype = WebSocket.prototype;
+
+},{}],38:[function(_dereq_,module,exports){
+(function (global){
+
+/*
+ * Module requirements.
+ */
+
+var isArray = _dereq_('isarray');
+
+/**
+ * Module exports.
+ */
+
+module.exports = hasBinary;
+
+/**
+ * Checks for binary data.
+ *
+ * Right now only Buffer and ArrayBuffer are supported..
+ *
+ * @param {Object} anything
+ * @api public
+ */
+
+function hasBinary(data) {
+
+  function _hasBinary(obj) {
+    if (!obj) return false;
+
+    if ( (global.Buffer && global.Buffer.isBuffer(obj)) ||
+         (global.ArrayBuffer && obj instanceof ArrayBuffer) ||
+         (global.Blob && obj instanceof Blob) ||
+         (global.File && obj instanceof File)
+        ) {
+      return true;
+    }
+
+    if (isArray(obj)) {
+      for (var i = 0; i < obj.length; i++) {
+          if (_hasBinary(obj[i])) {
+              return true;
+          }
+      }
+    } else if (obj && 'object' == typeof obj) {
+      if (obj.toJSON) {
+        obj = obj.toJSON();
+      }
+
+      for (var key in obj) {
+        if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  return _hasBinary(data);
+}
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"isarray":39}],39:[function(_dereq_,module,exports){
+module.exports=_dereq_(32)
+},{}],40:[function(_dereq_,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var global = _dereq_('global');
+
+/**
+ * Module exports.
+ *
+ * Logic borrowed from Modernizr:
+ *
+ *   - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js
+ */
+
+try {
+  module.exports = 'XMLHttpRequest' in global &&
+    'withCredentials' in new global.XMLHttpRequest();
+} catch (err) {
+  // if XMLHttp support is disabled in IE then it will throw
+  // when trying to create
+  module.exports = false;
+}
+
+},{"global":41}],41:[function(_dereq_,module,exports){
+
+/**
+ * Returns `this`. Execute this without a "context" (i.e. without it being
+ * attached to an object of the left-hand side), and `this` points to the
+ * "global" scope of the current JS execution.
+ */
+
+module.exports = (function () { return this; })();
+
+},{}],42:[function(_dereq_,module,exports){
+
+var indexOf = [].indexOf;
+
+module.exports = function(arr, obj){
+  if (indexOf) return arr.indexOf(obj);
+  for (var i = 0; i < arr.length; ++i) {
+    if (arr[i] === obj) return i;
+  }
+  return -1;
+};
+},{}],43:[function(_dereq_,module,exports){
+
+/**
+ * HOP ref.
+ */
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * Return own keys in `obj`.
+ *
+ * @param {Object} obj
+ * @return {Array}
+ * @api public
+ */
+
+exports.keys = Object.keys || function(obj){
+  var keys = [];
+  for (var key in obj) {
+    if (has.call(obj, key)) {
+      keys.push(key);
+    }
+  }
+  return keys;
+};
+
+/**
+ * Return own values in `obj`.
+ *
+ * @param {Object} obj
+ * @return {Array}
+ * @api public
+ */
+
+exports.values = function(obj){
+  var vals = [];
+  for (var key in obj) {
+    if (has.call(obj, key)) {
+      vals.push(obj[key]);
+    }
+  }
+  return vals;
+};
+
+/**
+ * Merge `b` into `a`.
+ *
+ * @param {Object} a
+ * @param {Object} b
+ * @return {Object} a
+ * @api public
+ */
+
+exports.merge = function(a, b){
+  for (var key in b) {
+    if (has.call(b, key)) {
+      a[key] = b[key];
+    }
+  }
+  return a;
+};
+
+/**
+ * Return length of `obj`.
+ *
+ * @param {Object} obj
+ * @return {Number}
+ * @api public
+ */
+
+exports.length = function(obj){
+  return exports.keys(obj).length;
+};
+
+/**
+ * Check if `obj` is empty.
+ *
+ * @param {Object} obj
+ * @return {Boolean}
+ * @api public
+ */
+
+exports.isEmpty = function(obj){
+  return 0 == exports.length(obj);
+};
+},{}],44:[function(_dereq_,module,exports){
+/**
+ * Parses an URI
+ *
+ * @author Steven Levithan <stevenlevithan.com> (MIT license)
+ * @api private
+ */
+
+var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
+
+var parts = [
+    'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host'
+  , 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
+];
+
+module.exports = function parseuri(str) {
+  var m = re.exec(str || '')
+    , uri = {}
+    , i = 14;
+
+  while (i--) {
+    uri[parts[i]] = m[i] || '';
+  }
+
+  return uri;
+};
+
+},{}],45:[function(_dereq_,module,exports){
+(function (global){
+/*global Blob,File*/
+
+/**
+ * Module requirements
+ */
+
+var isArray = _dereq_('isarray');
+var isBuf = _dereq_('./is-buffer');
+
+/**
+ * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
+ * Anything with blobs or files should be fed through removeBlobs before coming
+ * here.
+ *
+ * @param {Object} packet - socket.io event packet
+ * @return {Object} with deconstructed packet and list of buffers
+ * @api public
+ */
+
+exports.deconstructPacket = function(packet){
+  var buffers = [];
+  var packetData = packet.data;
+
+  function _deconstructPacket(data) {
+    if (!data) return data;
+
+    if (isBuf(data)) {
+      var placeholder = { _placeholder: true, num: buffers.length };
+      buffers.push(data);
+      return placeholder;
+    } else if (isArray(data)) {
+      var newData = new Array(data.length);
+      for (var i = 0; i < data.length; i++) {
+        newData[i] = _deconstructPacket(data[i]);
+      }
+      return newData;
+    } else if ('object' == typeof data && !(data instanceof Date)) {
+      var newData = {};
+      for (var key in data) {
+        newData[key] = _deconstructPacket(data[key]);
+      }
+      return newData;
+    }
+    return data;
+  }
+
+  var pack = packet;
+  pack.data = _deconstructPacket(packetData);
+  pack.attachments = buffers.length; // number of binary 'attachments'
+  return {packet: pack, buffers: buffers};
+};
+
+/**
+ * Reconstructs a binary packet from its placeholder packet and buffers
+ *
+ * @param {Object} packet - event packet with placeholders
+ * @param {Array} buffers - binary buffers to put in placeholder positions
+ * @return {Object} reconstructed packet
+ * @api public
+ */
+
+exports.reconstructPacket = function(packet, buffers) {
+  var curPlaceHolder = 0;
+
+  function _reconstructPacket(data) {
+    if (data && data._placeholder) {
+      var buf = buffers[data.num]; // appropriate buffer (should be natural order anyway)
+      return buf;
+    } else if (isArray(data)) {
+      for (var i = 0; i < data.length; i++) {
+        data[i] = _reconstructPacket(data[i]);
+      }
+      return data;
+    } else if (data && 'object' == typeof data) {
+      for (var key in data) {
+        data[key] = _reconstructPacket(data[key]);
+      }
+      return data;
+    }
+    return data;
+  }
+
+  packet.data = _reconstructPacket(packet.data);
+  packet.attachments = undefined; // no longer useful
+  return packet;
+};
+
+/**
+ * Asynchronously removes Blobs or Files from data via
+ * FileReader's readAsArrayBuffer method. Used before encoding
+ * data as msgpack. Calls callback with the blobless data.
+ *
+ * @param {Object} data
+ * @param {Function} callback
+ * @api private
+ */
+
+exports.removeBlobs = function(data, callback) {
+  function _removeBlobs(obj, curKey, containingObject) {
+    if (!obj) return obj;
+
+    // convert any blob
+    if ((global.Blob && obj instanceof Blob) ||
+        (global.File && obj instanceof File)) {
+      pendingBlobs++;
+
+      // async filereader
+      var fileReader = new FileReader();
+      fileReader.onload = function() { // this.result == arraybuffer
+        if (containingObject) {
+          containingObject[curKey] = this.result;
+        }
+        else {
+          bloblessData = this.result;
+        }
+
+        // if nothing pending its callback time
+        if(! --pendingBlobs) {
+          callback(bloblessData);
+        }
+      };
+
+      fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer
+    } else if (isArray(obj)) { // handle array
+      for (var i = 0; i < obj.length; i++) {
+        _removeBlobs(obj[i], i, obj);
+      }
+    } else if (obj && 'object' == typeof obj && !isBuf(obj)) { // and object
+      for (var key in obj) {
+        _removeBlobs(obj[key], key, obj);
+      }
+    }
+  }
+
+  var pendingBlobs = 0;
+  var bloblessData = data;
+  _removeBlobs(bloblessData);
+  if (!pendingBlobs) {
+    callback(bloblessData);
+  }
+};
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./is-buffer":47,"isarray":48}],46:[function(_dereq_,module,exports){
+
+/**
+ * Module dependencies.
+ */
+
+var debug = _dereq_('debug')('socket.io-parser');
+var json = _dereq_('json3');
+var isArray = _dereq_('isarray');
+var Emitter = _dereq_('component-emitter');
+var binary = _dereq_('./binary');
+var isBuf = _dereq_('./is-buffer');
+
+/**
+ * Protocol version.
+ *
+ * @api public
+ */
+
+exports.protocol = 4;
+
+/**
+ * Packet types.
+ *
+ * @api public
+ */
+
+exports.types = [
+  'CONNECT',
+  'DISCONNECT',
+  'EVENT',
+  'BINARY_EVENT',
+  'ACK',
+  'BINARY_ACK',
+  'ERROR'
+];
+
+/**
+ * Packet type `connect`.
+ *
+ * @api public
+ */
+
+exports.CONNECT = 0;
+
+/**
+ * Packet type `disconnect`.
+ *
+ * @api public
+ */
+
+exports.DISCONNECT = 1;
+
+/**
+ * Packet type `event`.
+ *
+ * @api public
+ */
+
+exports.EVENT = 2;
+
+/**
+ * Packet type `ack`.
+ *
+ * @api public
+ */
+
+exports.ACK = 3;
+
+/**
+ * Packet type `error`.
+ *
+ * @api public
+ */
+
+exports.ERROR = 4;
+
+/**
+ * Packet type 'binary event'
+ *
+ * @api public
+ */
+
+exports.BINARY_EVENT = 5;
+
+/**
+ * Packet type `binary ack`. For acks with binary arguments.
+ *
+ * @api public
+ */
+
+exports.BINARY_ACK = 6;
+
+/**
+ * Encoder constructor.
+ *
+ * @api public
+ */
+
+exports.Encoder = Encoder;
+
+/**
+ * Decoder constructor.
+ *
+ * @api public
+ */
+
+exports.Decoder = Decoder;
+
+/**
+ * A socket.io Encoder instance
+ *
+ * @api public
+ */
+
+function Encoder() {}
+
+/**
+ * Encode a packet as a single string if non-binary, or as a
+ * buffer sequence, depending on packet type.
+ *
+ * @param {Object} obj - packet object
+ * @param {Function} callback - function to handle encodings (likely engine.write)
+ * @return Calls callback with Array of encodings
+ * @api public
+ */
+
+Encoder.prototype.encode = function(obj, callback){
+  debug('encoding packet %j', obj);
+
+  if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) {
+    encodeAsBinary(obj, callback);
+  }
+  else {
+    var encoding = encodeAsString(obj);
+    callback([encoding]);
+  }
+};
+
+/**
+ * Encode packet as string.
+ *
+ * @param {Object} packet
+ * @return {String} encoded
+ * @api private
+ */
+
+function encodeAsString(obj) {
+  var str = '';
+  var nsp = false;
+
+  // first is type
+  str += obj.type;
+
+  // attachments if we have them
+  if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) {
+    str += obj.attachments;
+    str += '-';
+  }
+
+  // if we have a namespace other than `/`
+  // we append it followed by a comma `,`
+  if (obj.nsp && '/' != obj.nsp) {
+    nsp = true;
+    str += obj.nsp;
+  }
+
+  // immediately followed by the id
+  if (null != obj.id) {
+    if (nsp) {
+      str += ',';
+      nsp = false;
+    }
+    str += obj.id;
+  }
+
+  // json data
+  if (null != obj.data) {
+    if (nsp) str += ',';
+    str += json.stringify(obj.data);
+  }
+
+  debug('encoded %j as %s', obj, str);
+  return str;
+}
+
+/**
+ * Encode packet as 'buffer sequence' by removing blobs, and
+ * deconstructing packet into object with placeholders and
+ * a list of buffers.
+ *
+ * @param {Object} packet
+ * @return {Buffer} encoded
+ * @api private
+ */
+
+function encodeAsBinary(obj, callback) {
+
+  function writeEncoding(bloblessData) {
+    var deconstruction = binary.deconstructPacket(bloblessData);
+    var pack = encodeAsString(deconstruction.packet);
+    var buffers = deconstruction.buffers;
+
+    buffers.unshift(pack); // add packet info to beginning of data list
+    callback(buffers); // write all the buffers
+  }
+
+  binary.removeBlobs(obj, writeEncoding);
+}
+
+/**
+ * A socket.io Decoder instance
+ *
+ * @return {Object} decoder
+ * @api public
+ */
+
+function Decoder() {
+  this.reconstructor = null;
+}
+
+/**
+ * Mix in `Emitter` with Decoder.
+ */
+
+Emitter(Decoder.prototype);
+
+/**
+ * Decodes an ecoded packet string into packet JSON.
+ *
+ * @param {String} obj - encoded packet
+ * @return {Object} packet
+ * @api public
+ */
+
+Decoder.prototype.add = function(obj) {
+  var packet;
+  if ('string' == typeof obj) {
+    packet = decodeString(obj);
+    if (exports.BINARY_EVENT == packet.type || exports.BINARY_ACK == packet.type) { // binary packet's json
+      this.reconstructor = new BinaryReconstructor(packet);
+
+      // no attachments, labeled binary but no binary data to follow
+      if (this.reconstructor.reconPack.attachments === 0) {
+        this.emit('decoded', packet);
+      }
+    } else { // non-binary full packet
+      this.emit('decoded', packet);
+    }
+  }
+  else if (isBuf(obj) || obj.base64) { // raw binary data
+    if (!this.reconstructor) {
+      throw new Error('got binary data when not reconstructing a packet');
+    } else {
+      packet = this.reconstructor.takeBinaryData(obj);
+      if (packet) { // received final buffer
+        this.reconstructor = null;
+        this.emit('decoded', packet);
+      }
+    }
+  }
+  else {
+    throw new Error('Unknown type: ' + obj);
+  }
+};
+
+/**
+ * Decode a packet String (JSON data)
+ *
+ * @param {String} str
+ * @return {Object} packet
+ * @api private
+ */
+
+function decodeString(str) {
+  var p = {};
+  var i = 0;
+
+  // look up type
+  p.type = Number(str.charAt(0));
+  if (null == exports.types[p.type]) return error();
+
+  // look up attachments if type binary
+  if (exports.BINARY_EVENT == p.type || exports.BINARY_ACK == p.type) {
+    var buf = '';
+    while (str.charAt(++i) != '-') {
+      buf += str.charAt(i);
+      if (i == str.length) break;
+    }
+    if (buf != Number(buf) || str.charAt(i) != '-') {
+      throw new Error('Illegal attachments');
+    }
+    p.attachments = Number(buf);
+  }
+
+  // look up namespace (if any)
+  if ('/' == str.charAt(i + 1)) {
+    p.nsp = '';
+    while (++i) {
+      var c = str.charAt(i);
+      if (',' == c) break;
+      p.nsp += c;
+      if (i == str.length) break;
+    }
+  } else {
+    p.nsp = '/';
+  }
+
+  // look up id
+  var next = str.charAt(i + 1);
+  if ('' !== next && Number(next) == next) {
+    p.id = '';
+    while (++i) {
+      var c = str.charAt(i);
+      if (null == c || Number(c) != c) {
+        --i;
+        break;
+      }
+      p.id += str.charAt(i);
+      if (i == str.length) break;
+    }
+    p.id = Number(p.id);
+  }
+
+  // look up json data
+  if (str.charAt(++i)) {
+    try {
+      p.data = json.parse(str.substr(i));
+    } catch(e){
+      return error();
+    }
+  }
+
+  debug('decoded %s as %j', str, p);
+  return p;
+}
+
+/**
+ * Deallocates a parser's resources
+ *
+ * @api public
+ */
+
+Decoder.prototype.destroy = function() {
+  if (this.reconstructor) {
+    this.reconstructor.finishedReconstruction();
+  }
+};
+
+/**
+ * A manager of a binary event's 'buffer sequence'. Should
+ * be constructed whenever a packet of type BINARY_EVENT is
+ * decoded.
+ *
+ * @param {Object} packet
+ * @return {BinaryReconstructor} initialized reconstructor
+ * @api private
+ */
+
+function BinaryReconstructor(packet) {
+  this.reconPack = packet;
+  this.buffers = [];
+}
+
+/**
+ * Method to be called when binary data received from connection
+ * after a BINARY_EVENT packet.
+ *
+ * @param {Buffer | ArrayBuffer} binData - the raw binary data received
+ * @return {null | Object} returns null if more binary data is expected or
+ *   a reconstructed packet object if all buffers have been received.
+ * @api private
+ */
+
+BinaryReconstructor.prototype.takeBinaryData = function(binData) {
+  this.buffers.push(binData);
+  if (this.buffers.length == this.reconPack.attachments) { // done with buffer list
+    var packet = binary.reconstructPacket(this.reconPack, this.buffers);
+    this.finishedReconstruction();
+    return packet;
+  }
+  return null;
+};
+
+/**
+ * Cleans up binary packet reconstruction variables.
+ *
+ * @api private
+ */
+
+BinaryReconstructor.prototype.finishedReconstruction = function() {
+  this.reconPack = null;
+  this.buffers = [];
+};
+
+function error(data){
+  return {
+    type: exports.ERROR,
+    data: 'parser error'
+  };
+}
+
+},{"./binary":45,"./is-buffer":47,"component-emitter":9,"debug":10,"isarray":48,"json3":49}],47:[function(_dereq_,module,exports){
+(function (global){
+
+module.exports = isBuf;
+
+/**
+ * Returns true if obj is a buffer or an arraybuffer.
+ *
+ * @api private
+ */
+
+function isBuf(obj) {
+  return (global.Buffer && global.Buffer.isBuffer(obj)) ||
+         (global.ArrayBuffer && obj instanceof ArrayBuffer);
+}
+
+}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],48:[function(_dereq_,module,exports){
+module.exports=_dereq_(32)
+},{}],49:[function(_dereq_,module,exports){
+/*! JSON v3.2.6 | http://bestiejs.github.io/json3 | Copyright 2012-2013, Kit Cambridge | http://kit.mit-license.org */
+;(function (window) {
+  // Convenience aliases.
+  var getClass = {}.toString, isProperty, forEach, undef;
+
+  // Detect the `define` function exposed by asynchronous module loaders. The
+  // strict `define` check is necessary for compatibility with `r.js`.
+  var isLoader = typeof define === "function" && define.amd;
+
+  // Detect native implementations.
+  var nativeJSON = typeof JSON == "object" && JSON;
+
+  // Set up the JSON 3 namespace, preferring the CommonJS `exports` object if
+  // available.
+  var JSON3 = typeof exports == "object" && exports && !exports.nodeType && exports;
+
+  if (JSON3 && nativeJSON) {
+    // Explicitly delegate to the native `stringify` and `parse`
+    // implementations in CommonJS environments.
+    JSON3.stringify = nativeJSON.stringify;
+    JSON3.parse = nativeJSON.parse;
+  } else {
+    // Export for web browsers, JavaScript engines, and asynchronous module
+    // loaders, using the global `JSON` object if available.
+    JSON3 = window.JSON = nativeJSON || {};
+  }
+
+  // Test the `Date#getUTC*` methods. Based on work by @Yaffle.
+  var isExtended = new Date(-3509827334573292);
+  try {
+    // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
+    // results for certain dates in Opera >= 10.53.
+    isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 &&
+      // Safari < 2.0.2 stores the internal millisecond time value correctly,
+      // but clips the values returned by the date methods to the range of
+      // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
+      isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
+  } catch (exception) {}
+
+  // Internal: Determines whether the native `JSON.stringify` and `parse`
+  // implementations are spec-compliant. Based on work by Ken Snyder.
+  function has(name) {
+    if (has[name] !== undef) {
+      // Return cached feature test result.
+      return has[name];
+    }
+
+    var isSupported;
+    if (name == "bug-string-char-index") {
+      // IE <= 7 doesn't support accessing string characters using square
+      // bracket notation. IE 8 only supports this for primitives.
+      isSupported = "a"[0] != "a";
+    } else if (name == "json") {
+      // Indicates whether both `JSON.stringify` and `JSON.parse` are
+      // supported.
+      isSupported = has("json-stringify") && has("json-parse");
+    } else {
+      var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';
+      // Test `JSON.stringify`.
+      if (name == "json-stringify") {
+        var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended;
+        if (stringifySupported) {
+          // A test function object with a custom `toJSON` method.
+          (value = function () {
+            return 1;
+          }).toJSON = value;
+          try {
+            stringifySupported =
+              // Firefox 3.1b1 and b2 serialize string, number, and boolean
+              // primitives as object literals.
+              stringify(0) === "0" &&
+              // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
+              // literals.
+              stringify(new Number()) === "0" &&
+              stringify(new String()) == '""' &&
+              // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
+              // does not define a canonical JSON representation (this applies to
+              // objects with `toJSON` properties as well, *unless* they are nested
+              // within an object or array).
+              stringify(getClass) === undef &&
+              // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
+              // FF 3.1b3 pass this test.
+              stringify(undef) === undef &&
+              // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
+              // respectively, if the value is omitted entirely.
+              stringify() === undef &&
+              // FF 3.1b1, 2 throw an error if the given value is not a number,
+              // string, array, object, Boolean, or `null` literal. This applies to
+              // objects with custom `toJSON` methods as well, unless they are nested
+              // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
+              // methods entirely.
+              stringify(value) === "1" &&
+              stringify([value]) == "[1]" &&
+              // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
+              // `"[null]"`.
+              stringify([undef]) == "[null]" &&
+              // YUI 3.0.0b1 fails to serialize `null` literals.
+              stringify(null) == "null" &&
+              // FF 3.1b1, 2 halts serialization if an array contains a function:
+              // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3
+              // elides non-JSON values from objects and arrays, unless they
+              // define custom `toJSON` methods.
+              stringify([undef, getClass, null]) == "[null,null,null]" &&
+              // Simple serialization test. FF 3.1b1 uses Unicode escape sequences
+              // where character escape codes are expected (e.g., `\b` => `\u0008`).
+              stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized &&
+              // FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
+              stringify(null, value) === "1" &&
+              stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" &&
+              // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
+              // serialize extended years.
+              stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
+              // The milliseconds are optional in ES 5, but required in 5.1.
+              stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
+              // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
+              // four-digit years instead of six-digit years. Credits: @Yaffle.
+              stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
+              // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
+              // values less than 1000. Credits: @Yaffle.
+              stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
+          } catch (exception) {
+            stringifySupported = false;
+          }
+        }
+        isSupported = stringifySupported;
+      }
+      // Test `JSON.parse`.
+      if (name == "json-parse") {
+        var parse = JSON3.parse;
+        if (typeof parse == "function") {
+          try {
+            // FF 3.1b1, b2 will throw an exception if a bare literal is provided.
+            // Conforming implementations should also coerce the initial argument to
+            // a string prior to parsing.
+            if (parse("0") === 0 && !parse(false)) {
+              // Simple parsing test.
+              value = parse(serialized);
+              var parseSupported = value["a"].length == 5 && value["a"][0] === 1;
+              if (parseSupported) {
+                try {
+                  // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
+                  parseSupported = !parse('"\t"');
+                } catch (exception) {}
+                if (parseSupported) {
+                  try {
+                    // FF 4.0 and 4.0.1 allow leading `+` signs and leading
+                    // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow
+                    // certain octal literals.
+                    parseSupported = parse("01") !== 1;
+                  } catch (exception) {}
+                }
+                if (parseSupported) {
+                  try {
+                    // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal
+                    // points. These environments, along with FF 3.1b1 and 2,
+                    // also allow trailing commas in JSON objects and arrays.
+                    parseSupported = parse("1.") !== 1;
+                  } catch (exception) {}
+                }
+              }
+            }
+          } catch (exception) {
+            parseSupported = false;
+          }
+        }
+        isSupported = parseSupported;
+      }
+    }
+    return has[name] = !!isSupported;
+  }
+
+  if (!has("json")) {
+    // Common `[[Class]]` name aliases.
+    var functionClass = "[object Function]";
+    var dateClass = "[object Date]";
+    var numberClass = "[object Number]";
+    var stringClass = "[object String]";
+    var arrayClass = "[object Array]";
+    var booleanClass = "[object Boolean]";
+
+    // Detect incomplete support for accessing string characters by index.
+    var charIndexBuggy = has("bug-string-char-index");
+
+    // Define additional utility methods if the `Date` methods are buggy.
+    if (!isExtended) {
+      var floor = Math.floor;
+      // A mapping between the months of the year and the number of days between
+      // January 1st and the first of the respective month.
+      var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
+      // Internal: Calculates the number of days between the Unix epoch and the
+      // first day of the given month.
+      var getDay = function (year, month) {
+        return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
+      };
+    }
+
+    // Internal: Determines if a property is a direct property of the given
+    // object. Delegates to the native `Object#hasOwnProperty` method.
+    if (!(isProperty = {}.hasOwnProperty)) {
+      isProperty = function (property) {
+        var members = {}, constructor;
+        if ((members.__proto__ = null, members.__proto__ = {
+          // The *proto* property cannot be set multiple times in recent
+          // versions of Firefox and SeaMonkey.
+          "toString": 1
+        }, members).toString != getClass) {
+          // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
+          // supports the mutable *proto* property.
+          isProperty = function (property) {
+            // Capture and break the object's prototype chain (see section 8.6.2
+            // of the ES 5.1 spec). The parenthesized expression prevents an
+            // unsafe transformation by the Closure Compiler.
+            var original = this.__proto__, result = property in (this.__proto__ = null, this);
+            // Restore the original prototype chain.
+            this.__proto__ = original;
+            return result;
+          };
+        } else {
+          // Capture a reference to the top-level `Object` constructor.
+          constructor = members.constructor;
+          // Use the `constructor` property to simulate `Object#hasOwnProperty` in
+          // other environments.
+          isProperty = function (property) {
+            var parent = (this.constructor || constructor).prototype;
+            return property in this && !(property in parent && this[property] === parent[property]);
+          };
+        }
+        members = null;
+        return isProperty.call(this, property);
+      };
+    }
+
+    // Internal: A set of primitive types used by `isHostType`.
+    var PrimitiveTypes = {
+      'boolean': 1,
+      'number': 1,
+      'string': 1,
+      'undefined': 1
+    };
+
+    // Internal: Determines if the given object `property` value is a
+    // non-primitive.
+    var isHostType = function (object, property) {
+      var type = typeof object[property];
+      return type == 'object' ? !!object[property] : !PrimitiveTypes[type];
+    };
+
+    // Internal: Normalizes the `for...in` iteration algorithm across
+    // environments. Each enumerated key is yielded to a `callback` function.
+    forEach = function (object, callback) {
+      var size = 0, Properties, members, property;
+
+      // Tests for bugs in the current environment's `for...in` algorithm. The
+      // `valueOf` property inherits the non-enumerable flag from
+      // `Object.prototype` in older versions of IE, Netscape, and Mozilla.
+      (Properties = function () {
+        this.valueOf = 0;
+      }).prototype.valueOf = 0;
+
+      // Iterate over a new instance of the `Properties` class.
+      members = new Properties();
+      for (property in members) {
+        // Ignore all properties inherited from `Object.prototype`.
+        if (isProperty.call(members, property)) {
+          size++;
+        }
+      }
+      Properties = members = null;
+
+      // Normalize the iteration algorithm.
+      if (!size) {
+        // A list of non-enumerable properties inherited from `Object.prototype`.
+        members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"];
+        // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
+        // properties.
+        forEach = function (object, callback) {
+          var isFunction = getClass.call(object) == functionClass, property, length;
+          var hasProperty = !isFunction && typeof object.constructor != 'function' && isHostType(object, 'hasOwnProperty') ? object.hasOwnProperty : isProperty;
+          for (property in object) {
+            // Gecko <= 1.0 enumerates the `prototype` property of functions under
+            // certain conditions; IE does not.
+            if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) {
+              callback(property);
+            }
+          }
+          // Manually invoke the callback for each non-enumerable property.
+          for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property));
+        };
+      } else if (size == 2) {
+        // Safari <= 2.0.4 enumerates shadowed properties twice.
+        forEach = function (object, callback) {
+          // Create a set of iterated properties.
+          var members = {}, isFunction = getClass.call(object) == functionClass, property;
+          for (property in object) {
+            // Store each property name to prevent double enumeration. The
+            // `prototype` property of functions is not enumerated due to cross-
+            // environment inconsistencies.
+            if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) {
+              callback(property);
+            }
+          }
+        };
+      } else {
+        // No bugs detected; use the standard `for...in` algorithm.
+        forEach = function (object, callback) {
+          var isFunction = getClass.call(object) == functionClass, property, isConstructor;
+          for (property in object) {
+            if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) {
+              callback(property);
+            }
+          }
+          // Manually invoke the callback for the `constructor` property due to
+          // cross-environment inconsistencies.
+          if (isConstructor || isProperty.call(object, (property = "constructor"))) {
+            callback(property);
+          }
+        };
+      }
+      return forEach(object, callback);
+    };
+
+    // Public: Serializes a JavaScript `value` as a JSON string. The optional
+    // `filter` argument may specify either a function that alters how object and
+    // array members are serialized, or an array of strings and numbers that
+    // indicates which properties should be serialized. The optional `width`
+    // argument may be either a string or number that specifies the indentation
+    // level of the output.
+    if (!has("json-stringify")) {
+      // Internal: A map of control characters and their escaped equivalents.
+      var Escapes = {
+        92: "\\\\",
+        34: '\\"',
+        8: "\\b",
+        12: "\\f",
+        10: "\\n",
+        13: "\\r",
+        9: "\\t"
+      };
+
+      // Internal: Converts `value` into a zero-padded string such that its
+      // length is at least equal to `width`. The `width` must be <= 6.
+      var leadingZeroes = "000000";
+      var toPaddedString = function (width, value) {
+        // The `|| 0` expression is necessary to work around a bug in
+        // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
+        return (leadingZeroes + (value || 0)).slice(-width);
+      };
+
+      // Internal: Double-quotes a string `value`, replacing all ASCII control
+      // characters (characters with code unit values between 0 and 31) with
+      // their escaped equivalents. This is an implementation of the
+      // `Quote(value)` operation defined in ES 5.1 section 15.12.3.
+      var unicodePrefix = "\\u00";
+      var quote = function (value) {
+        var result = '"', index = 0, length = value.length, isLarge = length > 10 && charIndexBuggy, symbols;
+        if (isLarge) {
+          symbols = value.split("");
+        }
+        for (; index < length; index++) {
+          var charCode = value.charCodeAt(index);
+          // If the character is a control character, append its Unicode or
+          // shorthand escape sequence; otherwise, append the character as-is.
+          switch (charCode) {
+            case 8: case 9: case 10: case 12: case 13: case 34: case 92:
+              result += Escapes[charCode];
+              break;
+            default:
+              if (charCode < 32) {
+                result += unicodePrefix + toPaddedString(2, charCode.toString(16));
+                break;
+              }
+              result += isLarge ? symbols[index] : charIndexBuggy ? value.charAt(index) : value[index];
+          }
+        }
+        return result + '"';
+      };
+
+      // Internal: Recursively serializes an object. Implements the
+      // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
+      var serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
+        var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result;
+        try {
+          // Necessary for host object support.
+          value = object[property];
+        } catch (exception) {}
+        if (typeof value == "object" && value) {
+          className = getClass.call(value);
+          if (className == dateClass && !isProperty.call(value, "toJSON")) {
+            if (value > -1 / 0 && value < 1 / 0) {
+              // Dates are serialized according to the `Date#toJSON` method
+              // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
+              // for the ISO 8601 date time string format.
+              if (getDay) {
+                // Manually compute the year, month, date, hours, minutes,
+                // seconds, and milliseconds if the `getUTC*` methods are
+                // buggy. Adapted from @Yaffle's `date-shim` project.
+                date = floor(value / 864e5);
+                for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
+                for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
+                date = 1 + date - getDay(year, month);
+                // The `time` value specifies the time within the day (see ES
+                // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
+                // to compute `A modulo B`, as the `%` operator does not
+                // correspond to the `modulo` operation for negative numbers.
+                time = (value % 864e5 + 864e5) % 864e5;
+                // The hours, minutes, seconds, and milliseconds are obtained by
+                // decomposing the time within the day. See section 15.9.1.10.
+                hours = floor(time / 36e5) % 24;
+                minutes = floor(time / 6e4) % 60;
+                seconds = floor(time / 1e3) % 60;
+                milliseconds = time % 1e3;
+              } else {
+                year = value.getUTCFullYear();
+                month = value.getUTCMonth();
+                date = value.getUTCDate();
+                hours = value.getUTCHours();
+                minutes = value.getUTCMinutes();
+                seconds = value.getUTCSeconds();
+                milliseconds = value.getUTCMilliseconds();
+              }
+              // Serialize extended years correctly.
+              value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) +
+                "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) +
+                // Months, dates, hours, minutes, and seconds should have two
+                // digits; milliseconds should have three.
+                "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) +
+                // Milliseconds are optional in ES 5.0, but required in 5.1.
+                "." + toPaddedString(3, milliseconds) + "Z";
+            } else {
+              value = null;
+            }
+          } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) {
+            // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
+            // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
+            // ignores all `toJSON` methods on these objects unless they are
+            // defined directly on an instance.
+            value = value.toJSON(property);
+          }
+        }
+        if (callback) {
+          // If a replacement function was provided, call it to obtain the value
+          // for serialization.
+          value = callback.call(object, property, value);
+        }
+        if (value === null) {
+          return "null";
+        }
+        className = getClass.call(value);
+        if (className == booleanClass) {
+          // Booleans are represented literally.
+          return "" + value;
+        } else if (className == numberClass) {
+          // JSON numbers must be finite. `Infinity` and `NaN` are serialized as
+          // `"null"`.
+          return value > -1 / 0 && value < 1 / 0 ? "" + value : "null";
+        } else if (className == stringClass) {
+          // Strings are double-quoted and escaped.
+          return quote("" + value);
+        }
+        // Recursively serialize objects and arrays.
+        if (typeof value == "object") {
+          // Check for cyclic structures. This is a linear search; performance
+          // is inversely proportional to the number of unique nested objects.
+          for (length = stack.length; length--;) {
+            if (stack[length] === value) {
+              // Cyclic structures cannot be serialized by `JSON.stringify`.
+              throw TypeError();
+            }
+          }
+          // Add the object to the stack of traversed objects.
+          stack.push(value);
+          results = [];
+          // Save the current indentation level and indent one additional level.
+          prefix = indentation;
+          indentation += whitespace;
+          if (className == arrayClass) {
+            // Recursively serialize array elements.
+            for (index = 0, length = value.length; index < length; index++) {
+              element = serialize(index, value, callback, properties, whitespace, indentation, stack);
+              results.push(element === undef ? "null" : element);
+            }
+            result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
+          } else {
+            // Recursively serialize object members. Members are selected from
+            // either a user-specified list of property names, or the object
+            // itself.
+            forEach(properties || value, function (property) {
+              var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
+              if (element !== undef) {
+                // According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
+                // is not the empty string, let `member` {quote(property) + ":"}
+                // be the concatenation of `member` and the `space` character."
+                // The "`space` character" refers to the literal space
+                // character, not the `space` {width} argument provided to
+                // `JSON.stringify`.
+                results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
+              }
+            });
+            result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
+          }
+          // Remove the object from the traversed object stack.
+          stack.pop();
+          return result;
+        }
+      };
+
+      // Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
+      JSON3.stringify = function (source, filter, width) {
+        var whitespace, callback, properties, className;
+        if (typeof filter == "function" || typeof filter == "object" && filter) {
+          if ((className = getClass.call(filter)) == functionClass) {
+            callback = filter;
+          } else if (className == arrayClass) {
+            // Convert the property names array into a makeshift set.
+            properties = {};
+            for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1));
+          }
+        }
+        if (width) {
+          if ((className = getClass.call(width)) == numberClass) {
+            // Convert the `width` to an integer and create a string containing
+            // `width` number of space characters.
+            if ((width -= width % 1) > 0) {
+              for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " ");
+            }
+          } else if (className == stringClass) {
+            whitespace = width.length <= 10 ? width : width.slice(0, 10);
+          }
+        }
+        // Opera <= 7.54u2 discards the values associated with empty string keys
+        // (`""`) only if they are used directly within an object member list
+        // (e.g., `!("" in { "": 1})`).
+        return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []);
+      };
+    }
+
+    // Public: Parses a JSON source string.
+    if (!has("json-parse")) {
+      var fromCharCode = String.fromCharCode;
+
+      // Internal: A map of escaped control characters and their unescaped
+      // equivalents.
+      var Unescapes = {
+        92: "\\",
+        34: '"',
+        47: "/",
+        98: "\b",
+        116: "\t",
+        110: "\n",
+        102: "\f",
+        114: "\r"
+      };
+
+      // Internal: Stores the parser state.
+      var Index, Source;
+
+      // Internal: Resets the parser state and throws a `SyntaxError`.
+      var abort = function() {
+        Index = Source = null;
+        throw SyntaxError();
+      };
+
+      // Internal: Returns the next token, or `"$"` if the parser has reached
+      // the end of the source string. A token may be a string, number, `null`
+      // literal, or Boolean literal.
+      var lex = function () {
+        var source = Source, length = source.length, value, begin, position, isSigned, charCode;
+        while (Index < length) {
+          charCode = source.charCodeAt(Index);
+          switch (charCode) {
+            case 9: case 10: case 13: case 32:
+              // Skip whitespace tokens, including tabs, carriage returns, line
+              // feeds, and space characters.
+              Index++;
+              break;
+            case 123: case 125: case 91: case 93: case 58: case 44:
+              // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at
+              // the current position.
+              value = charIndexBuggy ? source.charAt(Index) : source[Index];
+              Index++;
+              return value;
+            case 34:
+              // `"` delimits a JSON string; advance to the next character and
+              // begin parsing the string. String tokens are prefixed with the
+              // sentinel `@` character to distinguish them from punctuators and
+              // end-of-string tokens.
+              for (value = "@", Index++; Index < length;) {
+                charCode = source.charCodeAt(Index);
+                if (charCode < 32) {
+                  // Unescaped ASCII control characters (those with a code unit
+                  // less than the space character) are not permitted.
+                  abort();
+                } else if (charCode == 92) {
+                  // A reverse solidus (`\`) marks the beginning of an escaped
+                  // control character (including `"`, `\`, and `/`) or Unicode
+                  // escape sequence.
+                  charCode = source.charCodeAt(++Index);
+                  switch (charCode) {
+                    case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114:
+                      // Revive escaped control characters.
+                      value += Unescapes[charCode];
+                      Index++;
+                      break;
+                    case 117:
+                      // `\u` marks the beginning of a Unicode escape sequence.
+                      // Advance to the first character and validate the
+                      // four-digit code point.
+                      begin = ++Index;
+                      for (position = Index + 4; Index < position; Index++) {
+                        charCode = source.charCodeAt(Index);
+                        // A valid sequence comprises four hexdigits (case-
+                        // insensitive) that form a single hexadecimal value.
+                        if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) {
+                          // Invalid Unicode escape sequence.
+                          abort();
+                        }
+                      }
+                      // Revive the escaped character.
+                      value += fromCharCode("0x" + source.slice(begin, Index));
+                      break;
+                    default:
+                      // Invalid escape sequence.
+                      abort();
+                  }
+                } else {
+                  if (charCode == 34) {
+                    // An unescaped double-quote character marks the end of the
+                    // string.
+                    break;
+                  }
+                  charCode = source.charCodeAt(Index);
+                  begin = Index;
+                  // Optimize for the common case where a string is valid.
+                  while (charCode >= 32 && charCode != 92 && charCode != 34) {
+                    charCode = source.charCodeAt(++Index);
+                  }
+                  // Append the string as-is.
+                  value += source.slice(begin, Index);
+                }
+              }
+              if (source.charCodeAt(Index) == 34) {
+                // Advance to the next character and return the revived string.
+                Index++;
+                return value;
+              }
+              // Unterminated string.
+              abort();
+            default:
+              // Parse numbers and literals.
+              begin = Index;
+              // Advance past the negative sign, if one is specified.
+              if (charCode == 45) {
+                isSigned = true;
+                charCode = source.charCodeAt(++Index);
+              }
+              // Parse an integer or floating-point value.
+              if (charCode >= 48 && charCode <= 57) {
+                // Leading zeroes are interpreted as octal literals.
+                if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) {
+                  // Illegal octal literal.
+                  abort();
+                }
+                isSigned = false;
+                // Parse the integer component.
+                for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++);
+                // Floats cannot contain a leading decimal point; however, this
+                // case is already accounted for by the parser.
+                if (source.charCodeAt(Index) == 46) {
+                  position = ++Index;
+                  // Parse the decimal component.
+                  for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++);
+                  if (position == Index) {
+                    // Illegal trailing decimal.
+                    abort();
+                  }
+                  Index = position;
+                }
+                // Parse exponents. The `e` denoting the exponent is
+                // case-insensitive.
+                charCode = source.charCodeAt(Index);
+                if (charCode == 101 || charCode == 69) {
+                  charCode = source.charCodeAt(++Index);
+                  // Skip past the sign following the exponent, if one is
+                  // specified.
+                  if (charCode == 43 || charCode == 45) {
+                    Index++;
+                  }
+                  // Parse the exponential component.
+                  for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++);
+                  if (position == Index) {
+                    // Illegal empty exponent.
+                    abort();
+                  }
+                  Index = position;
+                }
+                // Coerce the parsed value to a JavaScript number.
+                return +source.slice(begin, Index);
+              }
+              // A negative sign may only precede numbers.
+              if (isSigned) {
+                abort();
+              }
+              // `true`, `false`, and `null` literals.
+              if (source.slice(Index, Index + 4) == "true") {
+                Index += 4;
+                return true;
+              } else if (source.slice(Index, Index + 5) == "false") {
+                Index += 5;
+                return false;
+              } else if (source.slice(Index, Index + 4) == "null") {
+                Index += 4;
+                return null;
+              }
+              // Unrecognized token.
+              abort();
+          }
+        }
+        // Return the sentinel `$` character if the parser has reached the end
+        // of the source string.
+        return "$";
+      };
+
+      // Internal: Parses a JSON `value` token.
+      var get = function (value) {
+        var results, hasMembers;
+        if (value == "$") {
+          // Unexpected end of input.
+          abort();
+        }
+        if (typeof value == "string") {
+          if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") {
+            // Remove the sentinel `@` character.
+            return value.slice(1);
+          }
+          // Parse object and array literals.
+          if (value == "[") {
+            // Parses a JSON array, returning a new JavaScript array.
+            results = [];
+            for (;; hasMembers || (hasMembers = true)) {
+              value = lex();
+              // A closing square bracket marks the end of the array literal.
+              if (value == "]") {
+                break;
+              }
+              // If the array literal contains elements, the current token
+              // should be a comma separating the previous element from the
+              // next.
+              if (hasMembers) {
+                if (value == ",") {
+                  value = lex();
+                  if (value == "]") {
+                    // Unexpected trailing `,` in array literal.
+                    abort();
+                  }
+                } else {
+                  // A `,` must separate each array element.
+                  abort();
+                }
+              }
+              // Elisions and leading commas are not permitted.
+              if (value == ",") {
+                abort();
+              }
+              results.push(get(value));
+            }
+            return results;
+          } else if (value == "{") {
+            // Parses a JSON object, returning a new JavaScript object.
+            results = {};
+            for (;; hasMembers || (hasMembers = true)) {
+              value = lex();
+              // A closing curly brace marks the end of the object literal.
+              if (value == "}") {
+                break;
+              }
+              // If the object literal contains members, the current token
+              // should be a comma separator.
+              if (hasMembers) {
+                if (value == ",") {
+                  value = lex();
+                  if (value == "}") {
+                    // Unexpected trailing `,` in object literal.
+                    abort();
+                  }
+                } else {
+                  // A `,` must separate each object member.
+                  abort();
+                }
+              }
+              // Leading commas are not permitted, object property names must be
+              // double-quoted strings, and a `:` must separate each property
+              // name and value.
+              if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") {
+                abort();
+              }
+              results[value.slice(1)] = get(lex());
+            }
+            return results;
+          }
+          // Unexpected token encountered.
+          abort();
+        }
+        return value;
+      };
+
+      // Internal: Updates a traversed object member.
+      var update = function(source, property, callback) {
+        var element = walk(source, property, callback);
+        if (element === undef) {
+          delete source[property];
+        } else {
+          source[property] = element;
+        }
+      };
+
+      // Internal: Recursively traverses a parsed JSON object, invoking the
+      // `callback` function for each value. This is an implementation of the
+      // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
+      var walk = function (source, property, callback) {
+        var value = source[property], length;
+        if (typeof value == "object" && value) {
+          // `forEach` can't be used to traverse an array in Opera <= 8.54
+          // because its `Object#hasOwnProperty` implementation returns `false`
+          // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`).
+          if (getClass.call(value) == arrayClass) {
+            for (length = value.length; length--;) {
+              update(value, length, callback);
+            }
+          } else {
+            forEach(value, function (property) {
+              update(value, property, callback);
+            });
+          }
+        }
+        return callback.call(source, property, value);
+      };
+
+      // Public: `JSON.parse`. See ES 5.1 section 15.12.2.
+      JSON3.parse = function (source, callback) {
+        var result, value;
+        Index = 0;
+        Source = "" + source;
+        result = get(lex());
+        // If a JSON string contains multiple tokens, it is invalid.
+        if (lex() != "$") {
+          abort();
+        }
+        // Reset the parser state.
+        Index = Source = null;
+        return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result;
+      };
+    }
+  }
+
+  // Export for asynchronous module loaders.
+  if (isLoader) {
+    define(function () {
+      return JSON3;
+    });
+  }
+}(this));
+
+},{}],50:[function(_dereq_,module,exports){
+module.exports = toArray
+
+function toArray(list, index) {
+    var array = []
+
+    index = index || 0
+
+    for (var i = index || 0; i < list.length; i++) {
+        array[i - index] = list[i]
+    }
+
+    return array
+}
+
+},{}]},{},[1])
+(1)
+});
diff --git a/vendor/workerman/phpsocket.io/examples/chat/public/style.css b/vendor/workerman/phpsocket.io/examples/chat/public/style.css
new file mode 100644
index 0000000..3052d88
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/public/style.css
@@ -0,0 +1,149 @@
+/* Fix user-agent */
+
+* {
+  box-sizing: border-box;
+}
+
+html {
+  font-weight: 300;
+  -webkit-font-smoothing: antialiased;
+}
+
+html, input {
+  font-family:
+    "HelveticaNeue-Light",
+    "Helvetica Neue Light",
+    "Helvetica Neue",
+    Helvetica,
+    Arial,
+    "Lucida Grande",
+    sans-serif;
+}
+
+html, body {
+  height: 100%;
+  margin: 0;
+  padding: 0;
+}
+
+ul {
+  list-style: none;
+  word-wrap: break-word;
+}
+
+/* Pages */
+
+.pages {
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+}
+
+.page {
+  height: 100%;
+  position: absolute;
+  width: 100%;
+}
+
+/* Login Page */
+
+.login.page {
+  background-color: #000;
+}
+
+.login.page .form {
+  height: 100px;
+  margin-top: -100px;
+  position: absolute;
+
+  text-align: center;
+  top: 50%;
+  width: 100%;
+}
+
+.login.page .form .usernameInput {
+  background-color: transparent;
+  border: none;
+  border-bottom: 2px solid #fff;
+  outline: none;
+  padding-bottom: 15px;
+  text-align: center;
+  width: 400px;
+}
+
+.login.page .title {
+  font-size: 200%;
+}
+
+.login.page .usernameInput {
+  font-size: 200%;
+  letter-spacing: 3px;
+}
+
+.login.page .title, .login.page .usernameInput {
+  color: #fff;
+  font-weight: 100;
+}
+
+/* Chat page */
+
+.chat.page {
+  display: none;
+}
+
+/* Font */
+
+.messages {
+  font-size: 150%;
+}
+
+.inputMessage {
+  font-size: 100%;
+}
+
+.log {
+  color: gray;
+  font-size: 70%;
+  margin: 5px;
+  text-align: center;
+}
+
+/* Messages */
+
+.chatArea {
+  height: 100%;
+  padding-bottom: 60px;
+}
+
+.messages {
+  height: 100%;
+  margin: 0;
+  overflow-y: scroll;
+  padding: 10px 20px 10px 20px;
+}
+
+.message.typing .messageBody {
+  color: gray;
+}
+
+.username {
+  font-weight: 700;
+  overflow: hidden;
+  padding-right: 15px;
+  text-align: right;
+}
+
+/* Input */
+
+.inputMessage {
+  border: 10px solid #000;
+  bottom: 0;
+  height: 60px;
+  left: 0;
+  outline: none;
+  padding-left: 10px;
+  position: absolute;
+  right: 0;
+  width: 100%;
+}
diff --git a/vendor/workerman/phpsocket.io/examples/chat/start.php b/vendor/workerman/phpsocket.io/examples/chat/start.php
new file mode 100644
index 0000000..abb7d2a
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/start.php
@@ -0,0 +1,12 @@
+<?php
+use Workerman\Worker;
+use Workerman\WebServer;
+use Workerman\Autoloader;
+use PHPSocketIO\SocketIO;
+
+define('GLOBAL_START', true);
+
+require_once __DIR__ . '/start_web.php';
+require_once __DIR__ . '/start_io.php';
+
+Worker::runAll();
diff --git a/vendor/workerman/phpsocket.io/examples/chat/start_io.php b/vendor/workerman/phpsocket.io/examples/chat/start_io.php
new file mode 100644
index 0000000..fe491e2
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/start_io.php
@@ -0,0 +1,73 @@
+<?php
+use Workerman\Worker;
+use Workerman\WebServer;
+use Workerman\Autoloader;
+use PHPSocketIO\SocketIO;
+
+// composer autoload
+require_once join(DIRECTORY_SEPARATOR, array(__DIR__, "..", "..", "vendor", "autoload.php"));
+
+$io = new SocketIO(2020);
+$io->on('connection', function($socket){
+    $socket->addedUser = false;
+    // when the client emits 'new message', this listens and executes
+    $socket->on('new message', function ($data)use($socket){
+        // we tell the client to execute 'new message'
+        $socket->broadcast->emit('new message', array(
+            'username'=> $socket->username,
+            'message'=> $data
+        ));
+    });
+
+    // when the client emits 'add user', this listens and executes
+    $socket->on('add user', function ($username) use($socket){
+    if ($socket->addedUser)
+      return;
+        global $usernames, $numUsers;
+        // we store the username in the socket session for this client
+        $socket->username = $username;
+        ++$numUsers;
+        $socket->addedUser = true;
+        $socket->emit('login', array( 
+            'numUsers' => $numUsers
+        ));
+        // echo globally (all clients) that a person has connected
+        $socket->broadcast->emit('user joined', array(
+            'username' => $socket->username,
+            'numUsers' => $numUsers
+        ));
+    });
+
+    // when the client emits 'typing', we broadcast it to others
+    $socket->on('typing', function () use($socket) {
+        $socket->broadcast->emit('typing', array(
+            'username' => $socket->username
+        ));
+    });
+
+    // when the client emits 'stop typing', we broadcast it to others
+    $socket->on('stop typing', function () use($socket) {
+        $socket->broadcast->emit('stop typing', array(
+            'username' => $socket->username
+        ));
+    });
+
+    // when the user disconnects.. perform this
+    $socket->on('disconnect', function () use($socket) {
+        global $usernames, $numUsers;
+        if($socket->addedUser) {
+            --$numUsers;
+
+           // echo globally that this client has left
+           $socket->broadcast->emit('user left', array(
+               'username' => $socket->username,
+               'numUsers' => $numUsers
+            ));
+        }
+   });
+   
+});
+
+if (!defined('GLOBAL_START')) {
+    Worker::runAll();
+}
diff --git a/vendor/workerman/phpsocket.io/examples/chat/start_web.php b/vendor/workerman/phpsocket.io/examples/chat/start_web.php
new file mode 100644
index 0000000..eb46d70
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/examples/chat/start_web.php
@@ -0,0 +1,62 @@
+<?php
+use Workerman\Worker;
+use Workerman\Protocols\Http\Request;
+use Workerman\Protocols\Http\Response;
+use Workerman\Connection\TcpConnection;
+
+// composer autoload
+require_once join(DIRECTORY_SEPARATOR, array(__DIR__, "..", "..", "vendor", "autoload.php"));
+
+$web = new Worker('http://0.0.0.0:2022');
+$web->name = 'web';
+
+define('WEBROOT', __DIR__ . DIRECTORY_SEPARATOR .  'public');
+
+$web->onMessage = function (TcpConnection $connection, Request $request) {
+    $path = $request->path();
+    if ($path === '/') {
+        $connection->send(exec_php_file(WEBROOT.'/index.html'));
+        return;
+    }
+    $file = realpath(WEBROOT. $path);
+    if (false === $file) {
+        $connection->send(new Response(404, array(), '<h3>404 Not Found</h3>'));
+        return;
+    }
+    // Security check! Very important!!!
+    if (strpos($file, WEBROOT) !== 0) {
+        $connection->send(new Response(400));
+        return;
+    }
+    if (\pathinfo($file, PATHINFO_EXTENSION) === 'php') {
+        $connection->send(exec_php_file($file));
+        return;
+    }
+
+    $if_modified_since = $request->header('if-modified-since');
+    if (!empty($if_modified_since)) {
+        // Check 304.
+        $info = \stat($file);
+        $modified_time = $info ? \date('D, d M Y H:i:s', $info['mtime']) . ' ' . \date_default_timezone_get() : '';
+        if ($modified_time === $if_modified_since) {
+            $connection->send(new Response(304));
+            return;
+        }
+    }
+    $connection->send((new Response())->withFile($file));
+};
+
+function exec_php_file($file) {
+    \ob_start();
+    // Try to include php file.
+    try {
+        include $file;
+    } catch (\Exception $e) {
+        echo $e;
+    }
+    return \ob_get_clean();
+}
+
+if (!defined('GLOBAL_START')) {
+    Worker::runAll();
+}
diff --git a/vendor/workerman/phpsocket.io/src/ChannelAdapter.php b/vendor/workerman/phpsocket.io/src/ChannelAdapter.php
new file mode 100644
index 0000000..61a79d7
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/ChannelAdapter.php
@@ -0,0 +1,124 @@
+<?php
+namespace PHPSocketIO;
+class ChannelAdapter extends DefaultAdapter
+{
+    protected $_channelId = null;
+    
+    public static $ip = '127.0.0.1';
+    
+    public static $port = 2206;
+    
+    public function __construct($nsp)
+    {
+        parent::__construct($nsp);
+        $this->_channelId = (function_exists('random_int') ? random_int(1, 10000000): rand(1, 10000000)) . "-" . (function_exists('posix_getpid') ? posix_getpid(): 1);
+        \Channel\Client::connect(self::$ip, self::$port);
+        \Channel\Client::$onMessage = array($this, 'onChannelMessage');
+        \Channel\Client::subscribe("socket.io#/#");
+        Debug::debug('ChannelAdapter __construct');
+    }
+    
+    public function __destruct()
+    {
+        Debug::debug('ChannelAdapter __destruct');
+    }
+    
+    public function add($id ,$room)
+    {
+        $this->sids[$id][$room] = true;
+        $this->rooms[$room][$id] = true;
+        $channel = "socket.io#/#$room#";
+        \Channel\Client::subscribe($channel);
+    }
+    
+    public function del($id, $room)
+    {
+        unset($this->sids[$id][$room]);
+        unset($this->rooms[$room][$id]);
+        if(empty($this->rooms[$room]))
+        {
+            unset($this->rooms[$room]);
+            $channel = "socket.io#/#$room#";
+            \Channel\Client::unsubscribe($channel);
+        }
+    }
+    
+    public function delAll($id)
+    {
+        $rooms = isset($this->sids[$id]) ? array_keys($this->sids[$id]) : array();
+        if($rooms)
+        {
+            foreach($rooms as $room)
+            {
+                if(isset($this->rooms[$room][$id]))
+                {
+                    unset($this->rooms[$room][$id]);
+                    $channel = "socket.io#/#$room#";
+                    \Channel\Client::unsubscribe($channel);
+                }
+                if(isset($this->rooms[$room]) && empty($this->rooms[$room]))
+                {
+                    unset($this->rooms[$room]);
+                }
+            }
+        }
+        unset($this->sids[$id]);
+    }
+
+    public function onChannelMessage($channel, $msg)
+    {
+        if($this->_channelId === array_shift($msg))
+        {
+            //echo "ignore same channel_id \n";
+            return;
+        }
+        
+        $packet = $msg[0];
+        
+        $opts = $msg[1];
+        
+        if(!$packet)
+        {
+            echo "invalid  channel:$channel packet \n";
+            return;
+        }
+        
+        if(empty($packet['nsp'])) 
+        {
+            $packet['nsp'] = '/';
+        }
+        
+        if($packet['nsp'] != $this->nsp->name) 
+        {
+             echo "ignore different namespace {$packet['nsp']} != {$this->nsp->name}\n";
+             return;
+        }
+        
+        $this->broadcast($packet, $opts, true);
+    }
+    
+    public function broadcast($packet, $opts, $remote = false)
+    {
+        parent::broadcast($packet, $opts);
+        if (!$remote) 
+        {
+            $packet['nsp'] = '/';
+            
+            if(!empty($opts['rooms'])) 
+            {
+              foreach($opts['rooms'] as $room)
+              {
+                  $chn = "socket.io#/#$room#";
+                  $msg = array($this->_channelId, $packet, $opts);
+                  \Channel\Client::publish($chn, $msg);
+              }
+            }
+            else
+            {
+              $chn = "socket.io#/#";
+              $msg = array($this->_channelId, $packet, $opts);
+              \Channel\Client::publish($chn, $msg);
+            }
+        } 
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Client.php b/vendor/workerman/phpsocket.io/src/Client.php
new file mode 100644
index 0000000..5078f79
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Client.php
@@ -0,0 +1,260 @@
+<?php
+namespace PHPSocketIO;
+use PHPSocketIO\Parser\Parser;
+
+class Client
+{
+    public $server = null;
+    public $conn = null;
+    public $encoder = null;
+    public $decoder = null;
+    public $id = null;
+    public $request = null;
+    public $nsps = array();
+    public $connectBuffer = array();
+    public function __construct($server, $conn)
+    {
+        $this->server = $server;
+        $this->conn = $conn;
+        $this->encoder = new \PHPSocketIO\Parser\Encoder();
+        $this->decoder = new \PHPSocketIO\Parser\Decoder();
+        $this->id = $conn->id;
+        $this->request = $conn->request;
+        $this->setup();
+        Debug::debug('Client __construct');
+    }
+    
+public function __destruct()
+{
+    Debug::debug('Client __destruct');
+}
+
+/**
+ * Sets up event listeners.
+ *
+ * @api private
+ */
+
+    public function setup(){
+         $this->decoder->on('decoded', array($this,'ondecoded'));
+         $this->conn->on('data', array($this,'ondata'));
+         $this->conn->on('error', array($this, 'onerror'));
+         $this->conn->on('close' ,array($this, 'onclose'));
+    }
+
+/**
+ * Connects a client to a namespace.
+ *
+ * @param {String} namespace name
+ * @api private
+ */
+
+    public function connect($name){
+        if (!isset($this->server->nsps[$name])) 
+        {
+            $this->packet(array('type'=> Parser::ERROR, 'nsp'=> $name, 'data'=> 'Invalid namespace'));
+            return;
+        }
+        $nsp = $this->server->of($name);
+        if ('/' !== $name && !isset($this->nsps['/'])) 
+        {
+            $this->connectBuffer[$name] = $name;
+            return;
+        }
+        $nsp->add($this, $nsp, array($this, 'nspAdd'));
+    }
+
+    public function nspAdd($socket, $nsp)
+    {
+        $this->sockets[$socket->id] = $socket;
+        $this->nsps[$nsp->name] = $socket;
+        if ('/' === $nsp->name && $this->connectBuffer)
+        {
+            foreach($this->connectBuffer as $name)
+            {
+                $this->connect($name);
+            }
+            $this->connectBuffer = array();
+        }
+    }
+
+
+
+/**
+ * Disconnects from all namespaces and closes transport.
+ *
+ * @api private
+ */
+
+    public function disconnect()
+    {
+        foreach($this->sockets as $socket)
+        {
+            $socket->disconnect();
+        }
+        $this->sockets = array();
+        $this->close();
+    }
+
+/**
+ * Removes a socket. Called by each `Socket`.
+ *
+ * @api private
+ */
+
+    public function remove($socket)
+    {
+        if(isset($this->sockets[$socket->id]))
+        {
+            $nsp = $this->sockets[$socket->id]->nsp->name;
+            unset($this->sockets[$socket->id]);
+            unset($this->nsps[$nsp]);
+        } else {
+            //echo('ignoring remove for '. $socket->id);
+        }
+    }
+
+/**
+ * Closes the underlying connection.
+ *
+ * @api private
+ */
+
+    public function close()
+    {
+        if (empty($this->conn)) return;
+        if('open' === $this->conn->readyState) 
+        {
+             //echo('forcing transport close');
+             $this->conn->close();
+             $this->onclose('forced server close');
+        }
+    }
+
+/**
+ * Writes a packet to the transport.
+ *
+ * @param {Object} packet object
+ * @param {Object} options
+ * @api private
+ */
+    public function packet($packet, $preEncoded = false, $volatile = false)
+    {
+        if(!empty($this->conn) && 'open' === $this->conn->readyState) 
+        {
+            if (!$preEncoded) 
+            {
+                // not broadcasting, need to encode
+                $encodedPackets = $this->encoder->encode($packet);
+                $this->writeToEngine($encodedPackets, $volatile);
+            } else { // a broadcast pre-encodes a packet
+                 $this->writeToEngine($packet);
+            }
+        } else {
+            // todo check
+            // echo('ignoring packet write ' . var_export($packet, true));
+        }
+    }
+
+    public function  writeToEngine($encodedPackets, $volatile = false) 
+    {
+        if($volatile)echo new \Exception('volatile');
+        if ($volatile && !$this->conn->transport->writable) return;
+        // todo check
+        if(isset($encodedPackets['nsp']))unset($encodedPackets['nsp']);
+        foreach($encodedPackets as $packet) 
+        {
+             $this->conn->write($packet);
+        }
+    }
+
+
+/**
+ * Called with incoming transport data.
+ *
+ * @api private
+ */
+
+    public function ondata($data)
+    {
+        try {
+            // todo chek '2["chat message","2"]' . "\0" . '' 
+            $this->decoder->add(trim($data));
+        } catch(\Exception $e) {
+            $this->onerror($e);
+        }
+    }
+
+/**
+ * Called when parser fully decodes a packet.
+ *
+ * @api private
+ */
+
+    public function ondecoded($packet) 
+    {
+        if(Parser::CONNECT == $packet['type'])
+        {
+            $this->connect($packet['nsp']);
+        } else {
+            if(isset($this->nsps[$packet['nsp']])) 
+            {
+                 $this->nsps[$packet['nsp']]->onpacket($packet);
+            } else {
+                //echo('no socket for namespace ' . $packet['nsp']);
+            }
+        }
+    }
+
+/**
+ * Handles an error.
+ *
+ * @param {Objcet} error object
+ * @api private
+ */
+
+    public function onerror($err)
+    {
+        foreach($this->sockets as $socket)
+        {
+            $socket->onerror($err);
+        }
+        $this->onclose('client error');
+    }
+
+/**
+ * Called upon transport close.
+ *
+ * @param {String} reason
+ * @api private
+ */
+
+    public function onclose($reason)
+    {
+        if (empty($this->conn)) return;
+        // ignore a potential subsequent `close` event
+        $this->destroy();
+
+        // `nsps` and `sockets` are cleaned up seamlessly
+        foreach($this->sockets as $socket) 
+        {
+            $socket->onclose($reason);
+        }
+        $this->sockets = null;
+    }
+
+/**
+ * Cleans up event listeners.
+ *
+ * @api private
+ */
+
+    public function destroy()
+    {
+        if (!$this->conn) return;
+        $this->conn->removeAllListeners();
+        $this->decoder->removeAllListeners();
+        $this->encoder->removeAllListeners();
+        $this->server = $this->conn = $this->encoder = $this->decoder = $this->request = $this->nsps = null;
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Debug.php b/vendor/workerman/phpsocket.io/src/Debug.php
new file mode 100644
index 0000000..43a11f3
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Debug.php
@@ -0,0 +1,12 @@
+<?php
+namespace PHPSocketIO;
+
+class Debug 
+{
+    public static function debug($var)
+    {
+        global $debug;
+        if($debug)
+        echo var_export($var, true)."\n";
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/DefaultAdapter.php b/vendor/workerman/phpsocket.io/src/DefaultAdapter.php
new file mode 100644
index 0000000..3821707
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/DefaultAdapter.php
@@ -0,0 +1,106 @@
+<?php
+namespace PHPSocketIO;
+class DefaultAdapter
+{
+
+    public $nsp = null;
+    public $rooms = array();
+    public $sids = array();
+    public $encoder = null;
+    public function __construct($nsp)
+    {
+         $this->nsp = $nsp;
+         $this->encoder = new Parser\Encoder();
+         Debug::debug('DefaultAdapter __construct');
+    }
+    
+    public function __destruct()
+    {
+        Debug::debug('DefaultAdapter __destruct');
+    }
+    
+    public function add($id, $room)
+    {
+        $this->sids[$id][$room] = true;
+        $this->rooms[$room][$id] = true;
+    }
+     
+    public function del($id, $room)
+    {
+        unset($this->sids[$id][$room]);
+        unset($this->rooms[$room][$id]);
+        if(empty($this->rooms[$room]))
+        {
+            unset($this->rooms[$room]);
+        }
+    }
+
+    public function delAll($id)
+    {
+        $rooms = array_keys(isset($this->sids[$id]) ? $this->sids[$id] : array());
+        foreach($rooms as $room)
+        {
+            $this->del($id, $room);
+        }
+        unset($this->sids[$id]);
+    }
+
+    public function broadcast($packet, $opts, $remote = false)
+    {
+        $rooms = isset($opts['rooms']) ? $opts['rooms'] : array();
+        $except = isset($opts['except']) ? $opts['except'] : array();
+        $flags = isset($opts['flags']) ? $opts['flags'] : array();
+        $packetOpts = array(
+            'preEncoded' => true,
+            'volatile' => isset($flags['volatile']) ?  $flags['volatile'] : null,
+            'compress' => isset($flags['compress']) ? $flags['compress'] : null
+        );
+        $packet['nsp'] = $this->nsp->name;
+        $encodedPackets = $this->encoder->encode($packet);
+        if($rooms) 
+        {
+             $ids = array();
+             foreach($rooms as $i=>$room) 
+             {
+                 if(!isset($this->rooms[$room]))
+                 {
+                     continue;
+                 }
+                   
+                 $room = $this->rooms[$room];
+                 foreach($room as $id=>$item)
+                 {
+                    if(isset($ids[$id]) || isset($except[$id]))
+                    {
+                         continue;
+                    }
+                    if(isset($this->nsp->connected[$id]))
+                    {
+                         $ids[$id] = true;
+                         $this->nsp->connected[$id]->packet($encodedPackets, $packetOpts);
+                     }
+                 }
+             }
+         } else {
+             foreach($this->sids as $id=>$sid)
+             {
+                  if(isset($except[$id])) continue;
+                  if(isset($this->nsp->connected[$id]))
+                  {
+                      $socket = $this->nsp->connected[$id];
+                      $volatile = isset($flags['volatile']) ? $flags['volatile'] : null;
+                      $socket->packet($encodedPackets, true, $volatile);
+                  }
+              }
+         }
+    }
+    
+    public function clients($rooms, $fn) {
+        $sids = array();
+        foreach ($rooms as $room) {
+            $sids = array_merge($sids, $this->rooms[$room]);
+        }
+        $fn();
+    }
+
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Engine.php b/vendor/workerman/phpsocket.io/src/Engine/Engine.php
new file mode 100644
index 0000000..c890b90
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Engine.php
@@ -0,0 +1,309 @@
+<?php
+namespace PHPSocketIO\Engine;
+use \PHPSocketIO\Engine\Transports\Polling;
+use \PHPSocketIO\Engine\Transports\PollingXHR;
+use \PHPSocketIO\Engine\Transports\WebSocket;
+use \PHPSocketIO\Event\Emitter;
+use \PHPSocketIO\Debug;
+class Engine extends Emitter
+{
+    public $pingTimeout = 60;
+    public $pingInterval = 25;
+    public $upgradeTimeout = 5;
+    public $transports = array();
+    public $allowUpgrades = array();
+    public $allowRequest = array();
+    public $clients = array();
+    public $origins = '*:*';
+    public static $allowTransports = array(
+       'polling' => 'polling',
+       'websocket' => 'websocket'
+    );
+
+    public static $errorMessages = array(
+        'Transport unknown',
+        'Session ID unknown',
+        'Bad handshake method',
+        'Bad request'
+    );
+
+    const ERROR_UNKNOWN_TRANSPORT = 0;
+
+    const ERROR_UNKNOWN_SID = 1;
+
+    const ERROR_BAD_HANDSHAKE_METHOD = 2;
+
+    const ERROR_BAD_REQUEST = 3;
+
+    public function __construct($opts = array())
+    {
+        $ops_map = array(
+             'pingTimeout',
+             'pingInterval',
+             'upgradeTimeout',
+             'transports',
+             'allowUpgrades',
+             'allowRequest'
+        );
+        foreach($ops_map as $key)
+        {
+            if(isset($opts[$key]))
+            {
+                $this->$key = $opts[$key];
+            }
+        }
+        Debug::debug('Engine __construct');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('Engine __destruct');
+    }
+
+    public function handleRequest($req, $res)
+    {
+        $this->prepare($req);
+        $req->res = $res;
+        $this->verify($req, $res, false, array($this, 'dealRequest'));
+    }
+
+    public function dealRequest($err, $success, $req)
+    {
+        if (!$success)
+        {
+            self::sendErrorMessage($req, $req->res, $err);
+            return;
+        }
+
+        if(isset($req->_query['sid']))
+        {
+            $this->clients[$req->_query['sid']]->transport->onRequest($req);
+        }
+        else
+        {
+            $this->handshake($req->_query['transport'], $req);
+        }
+    }
+
+    protected function sendErrorMessage($req, $res, $code)
+    {
+        $headers = array('Content-Type'=> 'application/json');
+        if(isset($req->headers['origin']))
+        {
+            $headers['Access-Control-Allow-Credentials'] = 'true';
+            $headers['Access-Control-Allow-Origin'] = $req->headers['origin'];
+        } 
+        else 
+        {
+            $headers['Access-Control-Allow-Origin'] = '*';
+        }
+
+        $res->writeHead(403, '', $headers);
+        $res->end(json_encode(array(
+            'code' => $code,
+            'message' => isset(self::$errorMessages[$code]) ? self::$errorMessages[$code] : $code
+        )));
+    }
+
+    protected function verify($req, $res, $upgrade, $fn)
+    {
+        if(!isset($req->_query['transport']) || !isset(self::$allowTransports[$req->_query['transport']]))
+        {
+            return call_user_func($fn, self::ERROR_UNKNOWN_TRANSPORT, false, $req, $res);
+        }
+        $transport = $req->_query['transport'];
+        $sid = isset($req->_query['sid']) ? $req->_query['sid'] : '';
+        if ($transport === 'websocket' && empty($sid)) {
+            return call_user_func($fn, self::ERROR_UNKNOWN_TRANSPORT, false, $req, $res);
+        }
+        if($sid)
+        {
+            if(!isset($this->clients[$sid]))
+            {
+                return call_user_func($fn, self::ERROR_UNKNOWN_SID, false, $req, $res);
+            }
+            if(!$upgrade && $this->clients[$sid]->transport->name !== $transport)
+            {
+                return call_user_func($fn, self::ERROR_BAD_REQUEST, false, $req, $res);
+            }
+        }
+        else
+        {
+            if('GET' !== $req->method)
+            {
+               return call_user_func($fn, self::ERROR_BAD_HANDSHAKE_METHOD, false, $req, $res);
+            }
+            return $this->checkRequest($req, $res, $fn);
+        }
+        call_user_func($fn, null, true, $req, $res);
+    }
+
+    public function checkRequest($req, $res, $fn)
+    {
+        if ($this->origins === "*:*" || empty($this->origins))
+        {
+            return call_user_func($fn, null, true, $req, $res);
+        }
+        $origin = null;
+        if (isset($req->headers['origin']))
+        {
+            $origin = $req->headers['origin'];
+        }
+        else if(isset($req->headers['referer']))
+        {
+            $origin = $req->headers['referer'];
+        }
+
+        // file:// URLs produce a null Origin which can't be authorized via echo-back
+        if ('null' === $origin || null === $origin) {
+            return call_user_func($fn, null, true, $req, $res);
+        }
+
+        if ($origin)
+        {
+            $parts = parse_url($origin);
+            $defaultPort = 'https:' === $parts['scheme'] ? 443 : 80;
+            $parts['port'] = isset($parts['port']) ? $parts['port'] : $defaultPort;
+            $allowed_origins = explode(' ', $this->origins);
+            foreach( $allowed_origins as $allow_origin ){
+                $ok =
+                    $allow_origin === $parts['scheme'] . '://' . $parts['host'] . ':' . $parts['port'] ||
+                    $allow_origin === $parts['scheme'] . '://' . $parts['host'] ||
+                    $allow_origin === $parts['scheme'] . '://' . $parts['host'] . ':*' ||
+                    $allow_origin === '*:' . $parts['port'];
+                if($ok){
+                    # 只需要有一个白名单通过,则都通过
+                    return call_user_func($fn, null, $ok, $req, $res);
+                }
+                
+            }
+        }
+        call_user_func($fn, null, false, $req, $res);
+    }
+
+    protected function prepare($req)
+    {
+        if(!isset($req->_query))
+        {
+            $info = parse_url($req->url);
+            if(isset($info['query']))
+            {
+                parse_str($info['query'], $req->_query);
+            }
+        }
+    }
+
+    public function handshake($transport, $req)
+    {
+        $id = bin2hex(pack('d', microtime(true)).pack('N', function_exists('random_int') ? random_int(1, 100000000): rand(1, 100000000)));
+        if ($transport == 'websocket') {
+            $transport = '\\PHPSocketIO\\Engine\\Transports\\WebSocket';
+        }
+        elseif (isset($req->_query['j']))
+        {
+            $transport = '\\PHPSocketIO\\Engine\\Transports\\PollingJsonp';
+        } 
+        else 
+        {
+            $transport = '\\PHPSocketIO\\Engine\\Transports\\PollingXHR';
+        }
+
+        $transport = new $transport($req);
+        
+        $transport->supportsBinary = !isset($req->_query['b64']);
+
+        $socket = new Socket($id, $this, $transport, $req);
+        
+        /* $transport->on('headers', function(&$headers)use($id)
+        {
+            $headers['Set-Cookie'] = "io=$id";
+        }); */
+
+        $transport->onRequest($req);
+
+        $this->clients[$id] = $socket;
+        $socket->once('close', array($this, 'onSocketClose'));
+        $this->emit('connection', $socket);
+    }
+
+    public function onSocketClose($id)
+    {
+        unset($this->clients[$id]);
+    }
+
+    public function attach($worker)
+    {
+        $this->server = $worker;
+        $worker->onConnect = array($this, 'onConnect'); 
+    }
+    
+    public function onConnect($connection)
+    {
+        $connection->onRequest = array($this, 'handleRequest');
+        $connection->onWebSocketConnect = array($this, 'onWebSocketConnect');
+        // clean
+        $connection->onClose = function($connection)
+        {
+            if(!empty($connection->httpRequest))
+            {
+                $connection->httpRequest->destroy();
+                $connection->httpRequest = null;
+            }
+            if(!empty($connection->httpResponse))
+            {
+                $connection->httpResponse->destroy();
+                $connection->httpResponse = null;
+            }
+            if(!empty($connection->onRequest))
+            {
+                $connection->onRequest = null;
+            }
+            if(!empty($connection->onWebSocketConnect))
+            {
+                $connection->onWebSocketConnect = null;
+            }
+        };
+    }
+    
+    public function onWebSocketConnect($connection, $req, $res)
+    {
+        $this->prepare($req);
+        $this->verify($req, $res, true, array($this, 'dealWebSocketConnect'));
+    }
+
+    public function dealWebSocketConnect($err, $success, $req, $res)
+    {
+        if (!$success)
+        {
+            self::sendErrorMessage($req, $res, $err);
+            return;
+        }
+
+
+        if(isset($req->_query['sid']))
+        {
+            if(!isset($this->clients[$req->_query['sid']]))
+            {
+                self::sendErrorMessage($req, $res, 'upgrade attempt for closed client');
+                return;
+            }
+            $client = $this->clients[$req->_query['sid']];
+            if($client->upgrading)
+            {
+                self::sendErrorMessage($req, $res, 'transport has already been trying to upgrade');
+                return;
+            }
+            if($client->upgraded)
+            {
+                self::sendErrorMessage($req, $res, 'transport had already been upgraded');
+                return;
+            }
+            $transport = new WebSocket($req);
+            $client->maybeUpgrade($transport);
+        }
+        else
+        {
+            $this->handshake($req->_query['transport'], $req);
+        }
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Parser.php b/vendor/workerman/phpsocket.io/src/Engine/Parser.php
new file mode 100644
index 0000000..74913e4
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Parser.php
@@ -0,0 +1,303 @@
+<?php
+namespace PHPSocketIO\Engine;
+use \PHPSocketIO\Debug;
+class Parser
+{
+    public function __construct()
+{
+    Debug::debug('Engine/Parser __construct');    
+}
+
+
+    public static $packets=array(
+        'open'=>     0    // non-ws
+      , 'close'=>    1    // non-ws
+      , 'ping'=>     2
+      , 'pong'=>     3
+      , 'message'=>  4
+      , 'upgrade'=>  5
+      , 'noop'=>     6
+    );
+
+    public static $packetsList = array(
+       'open', 
+       'close',
+       'ping',
+       'pong',
+       'message',
+       'upgrade',
+       'noop'
+    );
+
+    public static $err = array(
+        'type' => 'error', 
+        'data' => 'parser error'
+    );
+
+    public static function encodePacket($packet)
+    {
+        $data = !isset($packet['data']) ? '' : $packet['data'];
+        return self::$packets[$packet['type']].$data;
+    }
+
+    
+    /**
+     * Encodes a packet with binary data in a base64 string
+     *
+     * @param {Object} packet, has `type` and `data`
+     * @return {String} base64 encoded message
+     */
+    
+    public static function encodeBase64Packet($packet)
+    {
+        $data = isset($packet['data'])  ? '' : $packet['data'];
+        return $message = 'b' . self::$packets[$packet['type']] . base64_encode($packet['data']);
+    }
+    
+    /**
+     * Decodes a packet. Data also available as an ArrayBuffer if requested.
+     *
+     * @return {Object} with `type` and `data` (if any)
+     * @api private
+     */
+    
+    public static function decodePacket($data, $binaryType = null, $utf8decode = true) 
+    {
+        // String data todo check if (typeof data == 'string' || data === undefined) 
+            if ($data[0] === 'b') 
+            {
+                return self::decodeBase64Packet(substr($data, 1), $binaryType);
+            }
+    
+            $type = $data[0];
+            if (!isset(self::$packetsList[$type]))
+            {
+                return self::$err;
+            }
+    
+            if (isset($data[1])) 
+            {
+                return array('type'=> self::$packetsList[$type], 'data'=> substr($data, 1));
+            } 
+            else 
+            {
+                return array('type'=> self::$packetsList[$type]);
+            }
+    }
+    
+    /**
+     * Decodes a packet encoded in a base64 string.
+     *
+     * @param {String} base64 encoded message
+     * @return {Object} with `type` and `data` (if any)
+     */
+    
+    public static function decodeBase64Packet($msg, $binaryType) 
+    {
+        $type = self::$packetsList[$msg[0]];
+        $data = base64_decode(substr($data, 1));
+        return array('type'=> $type, 'data'=> $data);
+    }
+    
+    /**
+     * Encodes multiple messages (payload).
+     *
+     *     <length>:data
+     *
+     * Example:
+     *
+     *     11:hello world2:hi
+     *
+     * If any contents are binary, they will be encoded as base64 strings. Base64
+     * encoded strings are marked with a b before the length specifier
+     *
+     * @param {Array} packets
+     * @api private
+     */
+    
+    public static function encodePayload($packets, $supportsBinary = null) 
+    {
+        if ($supportsBinary) 
+        {
+            return self::encodePayloadAsBinary($packets);
+        }
+    
+        if (!$packets) 
+        {
+            return '0:';
+        }
+        
+        $results = '';
+        foreach($packets as $msg)
+        {
+            $results .= self::encodeOne($msg, $supportsBinary);
+        }
+        return $results;
+    }
+    
+    
+    public static function encodeOne($packet, $supportsBinary = null, $result = null) 
+    {
+        $message = self::encodePacket($packet, $supportsBinary, true);
+        return strlen($message) . ':' . $message;
+    }
+    
+    
+    /*
+     * Decodes data when a payload is maybe expected. Possible binary contents are
+    * decoded from their base64 representation
+    *
+    * @api public
+    */
+    
+    public static function decodePayload($data, $binaryType = null) 
+    {
+        if(!preg_match('/^\d+:\d/',$data))
+        {
+            return self::decodePayloadAsBinary($data, $binaryType);
+        }
+    
+        if ($data === '') 
+        {
+            // parser error - ignoring payload
+            return self::$err;
+        }
+    
+        $length = '';//, n, msg;
+    
+        for ($i = 0, $l = strlen($data); $i < $l; $i++) 
+        {
+            $chr = $data[$i];
+    
+            if (':' != $chr) 
+            {
+                $length .= $chr;
+            } 
+            else 
+            {
+                if ('' == $length || ($length != ($n = intval($length)))) 
+                {
+                    // parser error - ignoring payload
+                    return self::$err;
+                }
+    
+                $msg = substr($data, $i + 1/*, $n*/);
+    
+                /*if ($length != strlen($msg)) 
+                {
+                    // parser error - ignoring payload
+                    return self::$err;
+                }*/
+    
+                if (isset($msg[0])) 
+                {
+                    $packet = self::decodePacket($msg, $binaryType, true);
+    
+                    if (self::$err['type'] == $packet['type'] && self::$err['data'] == $packet['data']) 
+                    {
+                        // parser error in individual packet - ignoring payload
+                        return self::$err;
+                    }
+    
+                    return $packet;
+                }
+    
+                // advance cursor
+                $i += $n;
+                $length = '';
+            }
+        }
+    
+        if ($length !== '') 
+        {
+            // parser error - ignoring payload
+            echo new \Exception('parser error');
+            return self::$err;
+        }
+    }
+    
+    /**
+     * Encodes multiple messages (payload) as binary.
+     *
+     * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
+     * 255><data>
+     *
+     * Example:
+     * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
+     *
+     * @param {Array} packets
+     * @return {Buffer} encoded payload
+     * @api private
+     */
+    
+    public static function encodePayloadAsBinary($packets) 
+    {
+        $results = '';
+        foreach($packets as $msg)
+        {
+            $results .= self::encodeOneAsBinary($msg);
+        }
+        return $results;
+    }
+    
+    public static function encodeOneAsBinary($p) 
+    {
+        // todo is string or arraybuf
+        $packet = self::encodePacket($p, true, true);
+        $encodingLength = ''.strlen($packet);
+        $sizeBuffer = chr(0);
+        for ($i = 0; $i < strlen($encodingLength); $i++)
+        {
+            $sizeBuffer .= chr($encodingLength[$i]);
+        }
+        $sizeBuffer .= chr(255);
+        return $sizeBuffer.$packet;
+    }
+    
+    /*
+     * Decodes data when a payload is maybe expected. Strings are decoded by
+    * interpreting each byte as a key code for entries marked to start with 0. See
+    * description of encodePayloadAsBinary
+    * @api public
+    */
+    
+    public static function decodePayloadAsBinary($data, $binaryType = null) 
+    {
+        $bufferTail = $data;
+        $buffers = array();
+    
+        while (strlen($bufferTail) > 0) 
+        {
+            $strLen = '';
+            $isString = $bufferTail[0] == 0;
+            $numberTooLong = false;
+            for ($i = 1; ; $i++) 
+            {
+                $tail = ord($bufferTail[$i]);
+                if ($tail === 255)  break;
+                // 310 = char length of Number.MAX_VALUE
+                if (strlen($strLen) > 310) 
+                {
+                    $numberTooLong = true;
+                    break;
+                }
+                $strLen .= $tail;
+            }
+            if($numberTooLong) return self::$err;
+            $bufferTail = substr($bufferTail, strlen($strLen) + 1);
+    
+            $msgLength = intval($strLen, 10);
+    
+            $msg = substr($bufferTail, 1, $msgLength + 1);
+            $buffers[] = $msg;
+            $bufferTail = substr($bufferTail, $msgLength + 1);
+        }
+        $total = count($buffers);
+        $packets = array();
+        foreach($buffers as $i => $buffer)
+        {
+            $packets[] = self::decodePacket($buffer, $binaryType, true);
+        }
+        return $packets;
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Request.php b/vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Request.php
new file mode 100644
index 0000000..ad197ad
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Request.php
@@ -0,0 +1,51 @@
+<?php
+namespace PHPSocketIO\Engine\Protocols\Http;
+
+class Request
+{
+    public $onData = null;
+
+    public $onEnd = null;
+
+    public $httpVersion = null;
+    
+    public $headers = array();
+    
+    public $rawHeaders = null;
+    
+    public $method = null;
+    
+    public $url = null;
+    
+    public $connection = null;
+    
+    public function __construct($connection, $raw_head)
+    {
+        $this->connection = $connection;
+        $this->parseHead($raw_head);
+    }
+    
+    public function parseHead($raw_head)
+    {
+        $header_data = explode("\r\n", $raw_head);
+        list($this->method, $this->url, $protocol) = explode(' ', $header_data[0]);
+        list($null, $this->httpVersion) = explode('/', $protocol);
+        unset($header_data[0]);
+        foreach($header_data as $content)
+        {
+            if(empty($content))
+            {
+                continue;
+            }
+            $this->rawHeaders[] = $content;
+            list($key, $value) = explode(':', $content, 2);
+            $this->headers[strtolower($key)] = trim($value);
+        }
+    }
+
+    public function destroy()
+    {
+        $this->onData = $this->onEnd = $this->onClose = null;
+        $this->connection = null;
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Response.php b/vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Response.php
new file mode 100644
index 0000000..d529642
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Response.php
@@ -0,0 +1,206 @@
+<?php
+namespace PHPSocketIO\Engine\Protocols\Http;
+
+class Response
+{
+    public $statusCode = 200;
+
+    protected $_statusPhrase = null;
+
+    protected $_connection = null;
+
+    protected $_headers = array();
+
+    public $headersSent = false;
+
+    public $writable = true;
+    
+    protected $_buffer = '';
+
+    public function __construct($connection)
+    {
+        $this->_connection = $connection;
+    }
+
+    protected function initHeader()
+    {
+        $this->_headers['Connection'] = 'keep-alive';
+        $this->_headers['Content-Type'] = 'Content-Type: text/html;charset=utf-8';
+    }
+
+    public function writeHead($status_code, $reason_phrase = '', $headers = null)
+    {
+        if($this->headersSent)
+        {
+            echo "header has already send\n";
+            return false;
+        }
+        $this->statusCode = $status_code;
+        if($reason_phrase)
+        {
+            $this->_statusPhrase = $reason_phrase;
+        }
+        if($headers)
+        {
+            foreach($headers as $key=>$val)
+            {
+                $this->_headers[$key] = $val;
+            }
+        }
+        $this->_buffer = $this->getHeadBuffer();
+        $this->headersSent = true;
+    }
+
+    public function getHeadBuffer()
+    {
+        if(!$this->_statusPhrase)
+        {
+            $this->_statusPhrase = isset(self::$codes[$this->statusCode]) ? self::$codes[$this->statusCode] : '';
+        }
+        $head_buffer = "HTTP/1.1 $this->statusCode $this->_statusPhrase\r\n";
+        if(!isset($this->_headers['Content-Length']) && !isset($this->_headers['Transfer-Encoding']))
+        {
+            $head_buffer .= "Transfer-Encoding: chunked\r\n";
+        }
+        if(!isset($this->_headers['Connection']))
+        {
+            $head_buffer .= "Connection: keep-alive\r\n";
+        }
+        foreach($this->_headers as $key=>$val)
+        {
+            if($key === 'Set-Cookie' && is_array($val))
+            {
+                foreach($val as $v)
+                {
+                    $head_buffer .= "Set-Cookie: $v\r\n";
+                }
+                continue;
+            }
+            $head_buffer .= "$key: $val\r\n";
+        }
+        return $head_buffer."\r\n";
+    }
+
+    public function setHeader($key, $val)
+    {
+        $this->_headers[$key] = $val;
+    }
+
+    public function getHeader($name)
+    {
+        return isset($this->_headers[$name]) ? $this->_headers[$name] : '';
+    }
+
+    public function removeHeader($name)
+    {
+        unset($this->_headers[$name]);
+    }
+
+    public function write($chunk)
+    {
+        if(!isset($this->_headers['Content-Length']))
+        {
+            $chunk = dechex(strlen($chunk)) . "\r\n" . $chunk . "\r\n";
+        }
+        if(!$this->headersSent)
+        {
+            $head_buffer = $this->getHeadBuffer();
+            $this->_buffer = $head_buffer . $chunk;
+            $this->headersSent = true;
+        }
+        else
+        {
+            $this->_buffer .= $chunk;
+        }
+    }
+     
+    public function end($data = null)
+    {
+        if(!$this->writable)
+        {
+            echo new \Exception('unwirtable');
+            return false;
+        }
+        if($data !== null)
+        {
+            $this->write($data);
+        }
+        
+        if(!$this->headersSent)
+        {
+            $head_buffer = $this->getHeadBuffer();
+            $this->_buffer = $head_buffer;
+            $this->headersSent = true;
+        }
+        
+        if(!isset($this->_headers['Content-Length']))
+        {
+            $ret = $this->_connection->send($this->_buffer . "0\r\n\r\n", true);
+            $this->destroy();
+            return $ret;
+        }
+        $ret = $this->_connection->send($this->_buffer, true);
+        $this->destroy();
+        return $ret;
+    }
+    
+    public function destroy()
+    {
+        if(!empty($this->_connection->httpRequest))
+        {
+            $this->_connection->httpRequest->destroy();
+        }
+        if(!empty($this->_connection))
+        {
+            $this->_connection->httpResponse = $this->_connection->httpRequest = null;
+        }
+        $this->_connection = null;
+        $this->writable = false;
+    }
+    
+    public static $codes = array(
+            100 => 'Continue',
+            101 => 'Switching Protocols',
+            200 => 'OK',
+            201 => 'Created',
+            202 => 'Accepted',
+            203 => 'Non-Authoritative Information',
+            204 => 'No Content',
+            205 => 'Reset Content',
+            206 => 'Partial Content',
+            300 => 'Multiple Choices',
+            301 => 'Moved Permanently',
+            302 => 'Found',
+            303 => 'See Other',
+            304 => 'Not Modified',
+            305 => 'Use Proxy',
+            306 => '(Unused)',
+            307 => 'Temporary Redirect',
+            400 => 'Bad Request',
+            401 => 'Unauthorized',
+            402 => 'Payment Required',
+            403 => 'Forbidden',
+            404 => 'Not Found',
+            405 => 'Method Not Allowed',
+            406 => 'Not Acceptable',
+            407 => 'Proxy Authentication Required',
+            408 => 'Request Timeout',
+            409 => 'Conflict',
+            410 => 'Gone',
+            411 => 'Length Required',
+            412 => 'Precondition Failed',
+            413 => 'Request Entity Too Large',
+            414 => 'Request-URI Too Long',
+            415 => 'Unsupported Media Type',
+            416 => 'Requested Range Not Satisfiable',
+            417 => 'Expectation Failed',
+            422 => 'Unprocessable Entity',
+            423 => 'Locked',
+            500 => 'Internal Server Error',
+            501 => 'Not Implemented',
+            502 => 'Bad Gateway',
+            503 => 'Service Unavailable',
+            504 => 'Gateway Timeout',
+            505 => 'HTTP Version Not Supported',
+    );
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Protocols/SocketIO.php b/vendor/workerman/phpsocket.io/src/Engine/Protocols/SocketIO.php
new file mode 100644
index 0000000..054b0c5
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Protocols/SocketIO.php
@@ -0,0 +1,209 @@
+<?php
+namespace PHPSocketIO\Engine\Protocols;
+use \PHPSocketIO\Engine\Protocols\WebSocket;
+use \PHPSocketIO\Engine\Protocols\Http\Request;
+use \PHPSocketIO\Engine\Protocols\Http\Response;
+use \Workerman\Connection\TcpConnection;
+class SocketIO 
+{
+    public static function input($http_buffer, $connection)
+    {
+        if(!empty($connection->hasReadedHead))
+        {
+            return strlen($http_buffer);
+        }
+        $pos = strpos($http_buffer, "\r\n\r\n"); 
+        if(!$pos)
+        {
+            if(strlen($http_buffer) >= $connection->maxPackageSize)
+            {
+                $connection->close("HTTP/1.1 400 bad request\r\n\r\nheader too long");
+                return 0;
+            }
+            return 0;
+        }
+        $head_len = $pos + 4;
+        $raw_head = substr($http_buffer, 0, $head_len);
+        $raw_body = substr($http_buffer, $head_len);
+        $req = new Request($connection, $raw_head);
+        $res = new Response($connection);
+        $connection->httpRequest = $req;
+        $connection->httpResponse = $res;
+        $connection->hasReadedHead = true;
+        TcpConnection::$statistics['total_request']++;
+        $connection->onClose = '\PHPSocketIO\Engine\Protocols\SocketIO::emitClose';
+        if(isset($req->headers['upgrade']) && strtolower($req->headers['upgrade']) === 'websocket')
+        {
+            $connection->consumeRecvBuffer(strlen($http_buffer));
+            WebSocket::dealHandshake($connection, $req, $res);
+            self::cleanup($connection);
+            return 0;
+        }
+        if(!empty($connection->onRequest))
+        {
+            $connection->consumeRecvBuffer(strlen($http_buffer));
+            self::emitRequest($connection, $req, $res);
+            if($req->method === 'GET' || $req->method === 'OPTIONS')
+            {
+                self::emitEnd($connection, $req);
+                return 0;
+            }
+
+            // POST
+            if('\PHPSocketIO\Engine\Protocols\SocketIO::onData' !== $connection->onMessage)
+            {
+                $connection->onMessage = '\PHPSocketIO\Engine\Protocols\SocketIO::onData';
+            }
+            if(!$raw_body)
+            {
+                return 0;
+            }
+            self::onData($connection, $raw_body);
+            return 0;
+        }
+        else
+        {
+            if($req->method === 'GET')
+            {
+                return $pos + 4;
+            }
+            elseif(isset($req->headers['content-length']))
+            {
+                return $req->headers['content-length'];
+            }
+            else
+            {
+                $connection->close("HTTP/1.1 400 bad request\r\n\r\ntrunk not support");
+                return 0; 
+            }
+        }
+    }
+    
+    public static function onData($connection, $data)
+    {
+        $req = $connection->httpRequest;
+        self::emitData($connection, $req, $data);
+        if((isset($req->headers['content-length']) && $req->headers['content-length'] <= strlen($data))     
+            || substr($data, -5) === "0\r\n\r\n")
+        {
+            self::emitEnd($connection, $req);
+        }
+    }
+
+    protected static function emitRequest($connection, $req, $res)
+    {
+        try
+        {
+            call_user_func($connection->onRequest, $req, $res);
+        }
+        catch(\Exception $e)
+        {
+            echo $e;
+        }
+    }
+    
+    public static function emitClose($connection)
+    {
+        $req = $connection->httpRequest;
+        if(isset($req->onClose))
+        {
+            try
+            {
+                call_user_func($req->onClose, $req);
+            }
+            catch(\Exception $e)
+            {
+                echo $e;
+            }
+        }
+        $res = $connection->httpResponse;
+        if(isset($res->onClose))
+        {
+            try
+            {
+                call_user_func($res->onClose, $res);
+            }
+            catch(\Exception $e)
+            {
+                echo $e;
+            }
+        }
+        self::cleanup($connection);
+    }
+
+    public static function cleanup($connection)
+    {
+        if(!empty($connection->onRequest))
+        {
+            $connection->onRequest = null;
+        }
+        if(!empty($connection->onWebSocketConnect))
+        {
+            $connection->onWebSocketConnect = null;
+        }
+        if(!empty($connection->httpRequest))
+        {
+            $connection->httpRequest->destroy();
+            $connection->httpRequest = null;
+        }
+        if(!empty($connection->httpResponse))
+        {
+            $connection->httpResponse->destroy();
+            $connection->httpResponse = null;
+        }
+    }
+    
+    public static function emitData($connection, $req, $data)
+    {
+        if(isset($req->onData))
+        {
+            try
+            {
+                call_user_func($req->onData, $req, $data);
+            }
+            catch(\Exception $e)
+            {
+                echo $e;
+            }
+        }
+    } 
+    
+    public static function emitEnd($connection, $req)
+    {
+        if(isset($req->onEnd))
+        {
+            try
+            {
+                call_user_func($req->onEnd, $req);
+            }
+            catch(\Exception $e)
+            {
+                echo $e;
+            }
+        }
+        $connection->hasReadedHead = false;
+    }
+
+    public static function encode($buffer, $connection)
+    {
+        if(!isset($connection->onRequest))
+        {
+            $connection->httpResponse->setHeader('Content-Length', strlen($buffer));
+            return $connection->httpResponse->getHeadBuffer() . $buffer;
+        }
+        return $buffer;
+    }
+
+    public static function decode($http_buffer, $connection)
+    {
+        if(isset($connection->onRequest))
+        {
+            return $http_buffer;
+        }
+        else
+        {
+            list($head, $body) = explode("\r\n\r\n", $http_buffer, 2);
+            return $body;
+        }
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket.php b/vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket.php
new file mode 100644
index 0000000..6a4dcf5
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket.php
@@ -0,0 +1,86 @@
+<?php 
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link http://www.workerman.net/
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace PHPSocketIO\Engine\Protocols;
+
+use \PHPSocketIO\Engine\Protocols\Http\Request;
+use \PHPSocketIO\Engine\Protocols\Http\Response;
+use \PHPSocketIO\Engine\Protocols\WebSocket\RFC6455;
+use \Workerman\Connection\TcpConnection;
+
+/**
+ * WebSocket 协议服务端解包和打包
+ */
+class WebSocket
+{
+    /**
+     * 最小包头
+     * @var int
+     */
+    const MIN_HEAD_LEN = 7;
+    
+    /**
+     * 检查包的完整性
+     * @param string $buffer
+     */
+    public static function input($buffer, $connection)
+    {
+        if(strlen($buffer) < self::MIN_HEAD_LEN)
+        {
+            return 0;
+        }
+        // flash policy file
+        if(0 === strpos($buffer,'<policy'))
+        {
+            $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'."\0";
+            $connection->send($policy_xml, true);
+            $connection->consumeRecvBuffer(strlen($buffer));
+            return 0;
+        }
+        // http head
+        $pos = strpos($buffer, "\r\n\r\n");
+        if(!$pos)
+        {
+            if(strlen($buffer)>=TcpConnection::$maxPackageSize)
+            {
+                $connection->close("HTTP/1.1 400 bad request\r\n\r\nheader too long");
+                return 0;
+            }
+            return 0;
+        }
+        $req = new Request($connection, $buffer);
+        $res = new Response($connection);
+        $connection->consumeRecvBuffer(strlen($buffer));
+        return self::dealHandshake($connection, $req, $res);
+        $connection->consumeRecvBuffer($pos+4);
+        return 0;
+    }
+
+    /**
+     * 处理websocket握手
+     * @param string $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function dealHandshake($connection, $req, $res)
+    {
+        if(isset($req->headers['sec-websocket-key1']))
+        {
+            $res->writeHead(400);
+            $res->end("Not support");
+            return 0;
+        }
+        $connection->protocol = 'PHPSocketIO\Engine\Protocols\WebSocket\RFC6455';
+        return RFC6455::dealHandshake($connection, $req, $res);
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket/RFC6455.php b/vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket/RFC6455.php
new file mode 100644
index 0000000..a7c61e3
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket/RFC6455.php
@@ -0,0 +1,335 @@
+<?php 
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link http://www.workerman.net/
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace PHPSocketIO\Engine\Protocols\WebSocket;
+
+use Workerman\Connection\ConnectionInterface;
+
+/**
+ * WebSocket 协议服务端解包和打包
+ */
+class RFC6455 implements \Workerman\Protocols\ProtocolInterface
+{
+    /**
+     * websocket头部最小长度
+     * @var int
+     */
+    const MIN_HEAD_LEN = 6;
+    
+    /**
+     * websocket blob类型
+     * @var char
+     */
+    const BINARY_TYPE_BLOB = "\x81";
+
+    /**
+     * websocket arraybuffer类型
+     * @var char
+     */
+    const BINARY_TYPE_ARRAYBUFFER = "\x82";
+    
+    /**
+     * 检查包的完整性
+     * @param string $buffer
+     */
+    public static function input($buffer, ConnectionInterface $connection)
+    {
+        // 数据长度
+        $recv_len = strlen($buffer);
+        // 长度不够
+        if($recv_len < self::MIN_HEAD_LEN)
+        {
+            return 0;
+        }
+        
+        // $connection->websocketCurrentFrameLength有值说明当前fin为0,则缓冲websocket帧数据
+        if($connection->websocketCurrentFrameLength)
+        {
+            // 如果当前帧数据未收全,则继续收
+            if($connection->websocketCurrentFrameLength > $recv_len)
+            {
+                // 返回0,因为不清楚完整的数据包长度,需要等待fin=1的帧
+                return 0;
+            }
+        }
+        else 
+        {
+            $data_len = ord($buffer[1]) & 127;
+            $firstbyte = ord($buffer[0]);
+            $is_fin_frame = $firstbyte>>7;
+            $opcode = $firstbyte & 0xf;
+            switch($opcode)
+            {
+                // 附加数据帧 @todo 实现附加数据帧
+                case 0x0:
+                    break;
+                // 文本数据帧
+                case 0x1:
+                    break;
+                // 二进制数据帧
+                case 0x2:
+                    break;
+                // 关闭的包
+                case 0x8:
+                    // 如果有设置onWebSocketClose回调,尝试执行
+                    if(isset($connection->onWebSocketClose))
+                    {
+                        call_user_func($connection->onWebSocketClose, $connection);
+                    }
+                    // 默认行为是关闭连接
+                    else
+                    {
+                        $connection->close();
+                    }
+                    return 0;
+                // ping的包
+                case 0x9:
+                    // 如果有设置onWebSocketPing回调,尝试执行
+                    if(isset($connection->onWebSocketPing))
+                    {
+                        call_user_func($connection->onWebSocketPing, $connection);
+                    }
+                    // 默认发送pong
+                    else 
+                    {
+                        $connection->send(pack('H*', '8a00'), true);
+                    }
+                    // 从接受缓冲区中消费掉该数据包
+                    if(!$data_len)
+                    {
+                        $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
+                        return 0;
+                    }
+                    break;
+                // pong的包
+                case 0xa:
+                    // 如果有设置onWebSocketPong回调,尝试执行
+                    if(isset($connection->onWebSocketPong))
+                    {
+                        call_user_func($connection->onWebSocketPong, $connection);
+                    }
+                    // 从接受缓冲区中消费掉该数据包
+                    if(!$data_len)
+                    {
+                        $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
+                        return 0;
+                    }
+                    break;
+                // 错误的opcode 
+                default :
+                    echo "error opcode $opcode and close websocket connection\n";
+                    $connection->close();
+                    return 0;
+            }
+            
+            // websocket二进制数据
+            $head_len = self::MIN_HEAD_LEN;
+            if ($data_len === 126) {
+                $head_len = 8;
+                if($head_len > $recv_len)
+                {
+                    return 0;
+                }
+                $pack = unpack('ntotal_len', substr($buffer, 2, 2));
+                $data_len = $pack['total_len'];
+            } else if ($data_len === 127) {
+                $head_len = 14;
+                if($head_len > $recv_len)
+                {
+                    return 0;
+                }
+                $arr = unpack('N2', substr($buffer, 2, 8));
+                $data_len = $arr[1]*4294967296 + $arr[2];
+            }
+            $current_frame_length = $head_len + $data_len;
+            if($is_fin_frame)
+            {
+                return $current_frame_length;
+            }
+            else
+            {
+                $connection->websocketCurrentFrameLength = $current_frame_length;
+            }
+        }
+        
+        // 收到的数据刚好是一个frame
+        if($connection->websocketCurrentFrameLength == $recv_len)
+        {
+            self::decode($buffer, $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $connection->websocketCurrentFrameLength = 0;
+            return 0;
+        }
+        // 收到的数据大于一个frame
+        elseif($connection->websocketCurrentFrameLength < $recv_len)
+        {
+            self::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $current_frame_length = $connection->websocketCurrentFrameLength;
+            $connection->websocketCurrentFrameLength = 0;
+            // 继续读取下一个frame
+            return self::input(substr($buffer, $current_frame_length), $connection);
+        }
+        // 收到的数据不足一个frame
+        else
+        {
+            return 0;
+        }
+    }
+    
+    /**
+     * 打包
+     * @param string $buffer
+     * @return string
+     */
+    public static function encode($buffer, ConnectionInterface $connection)
+    {
+        $len = strlen($buffer);
+        if(empty($connection->websocketHandshake))
+        {
+            // 默认是utf8文本格式
+            $connection->websocketType = self::BINARY_TYPE_BLOB;
+        }
+        
+        $first_byte = $connection->websocketType;
+        
+        if($len<=125)
+        {
+            $encode_buffer = $first_byte.chr($len).$buffer;
+        }
+        else if($len<=65535)
+        {
+            $encode_buffer = $first_byte.chr(126).pack("n", $len).$buffer;
+        }
+        else
+        {
+            $encode_buffer = $first_byte.chr(127).pack("xxxxN", $len).$buffer;
+        }
+        
+        // 还没握手不能发数据,先将数据缓冲起来,等握手完毕后发送
+        if(empty($connection->websocketHandshake))
+        {
+            if(empty($connection->websocketTmpData))
+            {
+                // 临时数据缓冲
+                $connection->websocketTmpData = '';
+            }
+            $connection->websocketTmpData .= $encode_buffer;
+            // 返回空,阻止发送
+            return '';
+        }
+        
+        return $encode_buffer;
+    }
+    
+    /**
+     * 解包
+     * @param string $buffer
+     * @return string
+     */
+    public static function decode($buffer, ConnectionInterface $connection)
+    {
+        $len = $masks = $data = $decoded = null;
+        $len = ord($buffer[1]) & 127;
+        if ($len === 126) {
+            $masks = substr($buffer, 4, 4);
+            $data = substr($buffer, 8);
+        } else if ($len === 127) {
+            $masks = substr($buffer, 10, 4);
+            $data = substr($buffer, 14);
+        } else {
+            $masks = substr($buffer, 2, 4);
+            $data = substr($buffer, 6);
+        }
+        for ($index = 0; $index < strlen($data); $index++) {
+            $decoded .= $data[$index] ^ $masks[$index % 4];
+        }
+        if($connection->websocketCurrentFrameLength)
+        {
+            $connection->websocketDataBuffer .= $decoded;
+            return $connection->websocketDataBuffer;
+        }
+        else
+        {
+            $decoded = $connection->websocketDataBuffer . $decoded;
+            $connection->websocketDataBuffer = '';
+            return $decoded;
+        }
+    }
+    
+    /**
+     * 处理websocket握手
+     * @param string $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function dealHandshake($connection, $req, $res)
+    {
+        $headers = array();
+        if(isset($connection->onWebSocketConnect))
+        {
+            try
+            {
+                call_user_func_array($connection->onWebSocketConnect, array($connection, $req, $res));
+            }
+            catch (\Exception $e)
+            {
+                echo $e;
+            }
+            if(!$res->writable)
+            {
+                return false;
+            }
+        }
+        
+        if(isset($req->headers['sec-websocket-key']))
+        {
+            $sec_websocket_key = $req->headers['sec-websocket-key'];
+        }
+        else
+        {
+            $res->writeHead(400);
+            $res->end('<b>400 Bad Request</b><br>Upgrade to websocket but Sec-WebSocket-Key not found.');
+            return 0;
+        }
+        
+        // 标记已经握手
+        $connection->websocketHandshake = true;
+        // 缓冲fin为0的包,直到fin为1
+        $connection->websocketDataBuffer = '';
+        // 当前数据帧的长度,可能是fin为0的帧,也可能是fin为1的帧
+        $connection->websocketCurrentFrameLength = 0;
+        // 当前帧的数据缓冲
+        $connection->websocketCurrentFrameBuffer = '';
+        // blob or arraybuffer
+        $connection->websocketType = self::BINARY_TYPE_BLOB;
+        
+        $sec_websocket_accept = base64_encode(sha1($sec_websocket_key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true));
+        $headers['Content-Length'] = 0;
+        $headers['Upgrade'] = 'websocket';
+        $headers['Sec-WebSocket-Version'] = 13;
+        $headers['Connection'] = 'Upgrade';
+        $headers['Sec-WebSocket-Accept'] = $sec_websocket_accept;
+        $res->writeHead(101, '', $headers);
+        $res->end();
+            
+        // 握手后有数据要发送
+        if(!empty($connection->websocketTmpData))
+        {
+            $connection->send($connection->websocketTmpData, true);
+            $connection->websocketTmpData = '';
+        }
+        
+        return 0;
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Socket.php b/vendor/workerman/phpsocket.io/src/Engine/Socket.php
new file mode 100644
index 0000000..0c55e6a
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Socket.php
@@ -0,0 +1,380 @@
+<?php
+namespace PHPSocketIO\Engine;
+use \PHPSocketIO\Event\Emitter;
+use \Workerman\Lib\Timer;
+use \PHPSocketIO\Debug;
+class Socket extends Emitter
+{
+    public $id = 0;
+    public $server = null;
+    public $upgrading = false;
+    public $upgraded = false;
+    public $readyState = 'opening';
+    public $writeBuffer = array();
+    public $packetsFn = array();
+    public $sentCallbackFn = array();
+    public $request = null;
+    public $remoteAddress = '';
+    public $checkIntervalTimer = null;
+    public $upgradeTimeoutTimer = null;
+    public $pingTimeoutTimer = null;
+
+    public function __construct($id, $server, $transport, $req)
+    {
+        $this->id = $id;
+        $this->server = $server;
+        $this->request = $req;
+        $this->remoteAddress = $req->connection->getRemoteIp().':'.$req->connection->getRemotePort();
+        $this->setTransport($transport);
+        $this->onOpen();
+        Debug::debug('Engine/Socket __construct');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('Engine/Socket __destruct');
+    }
+
+    public function maybeUpgrade($transport)
+    {
+        $this->upgrading = true;
+        $this->upgradeTimeoutTimer = Timer::add(
+            $this->server->upgradeTimeout, 
+            array($this, 'upgradeTimeoutCallback'),
+            array($transport), false
+        );
+        $this->upgradeTransport = $transport;
+        $transport->on('packet', array($this, 'onUpgradePacket'));
+        $transport->once('close', array($this, 'onUpgradeTransportClose'));
+        $transport->once('error', array($this, 'onUpgradeTransportError'));
+        $this->once('close', array($this, 'onUpgradeTransportClose'));
+    }
+
+    public function onUpgradePacket($packet)
+    {
+        if(empty($this->upgradeTransport))
+        {
+             $this->onError('upgradeTransport empty'); 
+             return;
+        }
+        if('ping' === $packet['type'] && (isset($packet['data']) && 'probe' === $packet['data']))
+        {
+            $this->upgradeTransport->send(array(array('type'=> 'pong', 'data'=> 'probe')));
+            //$this->transport->shouldClose = function(){};
+            if ($this->checkIntervalTimer) {
+		        Timer::del($this->checkIntervalTimer);
+	        }
+            $this->checkIntervalTimer = Timer::add(0.5, array($this, 'check'));
+        }
+        else if('upgrade' === $packet['type'] && $this->readyState !== 'closed')
+        {
+            $this->upgradeCleanup();
+            $this->upgraded = true;
+            $this->clearTransport();
+            $this->transport->destroy();
+            $this->setTransport($this->upgradeTransport);
+            $this->emit('upgrade', $this->upgradeTransport);
+            $this->upgradeTransport = null;
+            $this->setPingTimeout();
+            $this->flush();
+            if($this->readyState === 'closing')
+            {
+                $this->transport->close(array($this, 'onClose'));
+            }
+        }
+        else
+        {
+            if(!empty($this->upgradeTransport))
+            {
+                $this->upgradeCleanup();
+                $this->upgradeTransport->close();
+                $this->upgradeTransport = null;
+            }
+        }
+       
+    }
+
+
+    public function upgradeCleanup()
+    {
+        $this->upgrading = false;
+        Timer::del($this->checkIntervalTimer);
+        Timer::del($this->upgradeTimeoutTimer);
+        if(!empty($this->upgradeTransport))
+        {
+            $this->upgradeTransport->removeListener('packet', array($this, 'onUpgradePacket'));
+            $this->upgradeTransport->removeListener('close', array($this, 'onUpgradeTransportClose'));
+            $this->upgradeTransport->removeListener('error', array($this, 'onUpgradeTransportError'));
+        }
+        $this->removeListener('close', array($this, 'onUpgradeTransportClose'));
+    }
+
+    public function onUpgradeTransportClose()
+    {
+        $this->onUpgradeTransportError('transport closed');
+    }
+
+    public function onUpgradeTransportError($err)
+    {
+        //echo $err;
+        $this->upgradeCleanup();
+        if($this->upgradeTransport)
+        {
+            $this->upgradeTransport->close();
+            $this->upgradeTransport = null;
+        }
+    }
+
+    public function upgradeTimeoutCallback($transport)
+    {
+        //echo("client did not complete upgrade - closing transport\n");
+        $this->upgradeCleanup();
+        if('open' === $transport->readyState)
+        {
+             $transport->close();
+        }
+    }
+    
+    public function setTransport($transport)
+    {
+        $this->transport = $transport;
+        $this->transport->once('error', array($this, 'onError'));
+        $this->transport->on('packet', array($this, 'onPacket'));
+        $this->transport->on('drain', array($this, 'flush'));
+        $this->transport->once('close', array($this, 'onClose'));
+        //this function will manage packet events (also message callbacks)
+        $this->setupSendCallback();
+    }
+    
+    public function onOpen()
+    {
+        $this->readyState = 'open';
+        
+        // sends an `open` packet
+        $this->transport->sid = $this->id;
+        $this->sendPacket('open', json_encode(array(
+            'sid'=> $this->id
+            , 'upgrades' => $this->getAvailableUpgrades()
+            , 'pingInterval'=> $this->server->pingInterval*1000
+            , 'pingTimeout'=> $this->server->pingTimeout*1000
+        )));
+        
+        $this->emit('open');
+        $this->setPingTimeout();
+    }
+    
+    public function onPacket($packet)
+    {
+        if ('open' === $this->readyState) {
+            // export packet event
+            $this->emit('packet', $packet);
+        
+            // Reset ping timeout on any packet, incoming data is a good sign of
+            // other side's liveness
+            $this->setPingTimeout();
+            switch ($packet['type']) {
+        
+                case 'ping':
+                    $this->sendPacket('pong');
+                    $this->emit('heartbeat');
+                    break;
+        
+                case 'error':
+                    $this->onClose('parse error');
+                    break;
+        
+                case 'message':
+                    $this->emit('data', $packet['data']);
+                    $this->emit('message', $packet['data']);
+                    break;
+            }
+        } 
+        else 
+        {
+            echo('packet received with closed socket');
+        }
+    } 
+   
+    public function check()
+    {
+        if('polling' == $this->transport->name && $this->transport->writable)
+        {
+            $this->transport->send(array(array('type' => 'noop')));
+        }
+    }
+ 
+    public function onError($err) 
+    {
+        $this->onClose('transport error', $err);
+    }
+    
+    public function setPingTimeout()
+    {
+        if ($this->pingTimeoutTimer) {
+            Timer::del($this->pingTimeoutTimer);
+        }
+        $this->pingTimeoutTimer = Timer::add(
+           $this->server->pingInterval + $this->server->pingTimeout ,
+           array($this, 'pingTimeoutCallback'), null, false);
+    }
+
+    public function pingTimeoutCallback()
+    {
+        $this->transport->close();
+        $this->onClose('ping timeout');
+    }
+
+    
+    public function clearTransport()
+    {
+        $this->transport->close();
+        Timer::del($this->pingTimeoutTimer);
+    }
+    
+    public function onClose($reason = '', $description = null)
+    {
+        if ('closed' !== $this->readyState) 
+        {
+            Timer::del($this->pingTimeoutTimer);
+            Timer::del($this->checkIntervalTimer);
+            $this->checkIntervalTimer = null;
+            Timer::del($this->upgradeTimeoutTimer);
+            // clean writeBuffer in next tick, so developers can still
+            // grab the writeBuffer on 'close' event
+            $this->writeBuffer = array();
+            $this->packetsFn = array();
+            $this->sentCallbackFn = array();
+            $this->clearTransport();
+            $this->readyState = 'closed';
+            $this->emit('close', $this->id, $reason, $description);
+            $this->server = null;
+            $this->request = null;
+            $this->upgradeTransport = null;
+            $this->removeAllListeners();
+            if(!empty($this->transport))
+            {
+                $this->transport->removeAllListeners();
+                $this->transport = null;
+            }
+        }
+    }
+    
+    public function send($data, $options, $callback)
+    {
+        $this->sendPacket('message', $data, $callback);
+        return $this;
+    }
+    
+    public function write($data, $options = array(), $callback = null)
+    {
+        return $this->send($data, $options, $callback);
+    }
+    
+    public function sendPacket($type, $data = null, $callback = null)
+    {
+        if('closing' !== $this->readyState) 
+        {
+            $packet = array(
+                'type'=> $type
+            );
+            if($data !== null) 
+            {
+                $packet['data'] = $data;
+            }
+            // exports packetCreate event
+            $this->emit('packetCreate', $packet);
+            $this->writeBuffer[] = $packet;
+            //add send callback to object
+            if($callback)
+            {
+                $this->packetsFn[] = $callback;
+            }
+            $this->flush();
+        }
+    }
+    
+    public function flush() 
+    {
+        if ('closed' !== $this->readyState && $this->transport->writable
+        && $this->writeBuffer) 
+        {
+            $this->emit('flush', $this->writeBuffer);
+            $this->server->emit('flush', $this, $this->writeBuffer);
+            $wbuf = $this->writeBuffer;
+            $this->writeBuffer = array();
+            if($this->packetsFn)
+            {
+                if(!empty($this->transport->supportsFraming)) 
+                {
+                    $this->sentCallbackFn[] = $this->packetsFn;
+                } 
+                else 
+                {
+                   // @todo check
+                   $this->sentCallbackFn[]=$this->packetsFn;
+                }
+            }
+            $this->packetsFn = array();
+            $this->transport->send($wbuf);
+            $this->emit('drain');
+            if($this->server)
+            {
+                $this->server->emit('drain', $this);
+            }
+        }
+    }
+
+    public function getAvailableUpgrades()
+    {
+        return array('websocket');
+    }
+
+    public function close()
+    {
+        if ('open' !== $this->readyState)
+        {
+            return;
+        }
+   
+        $this->readyState = 'closing';
+
+        if ($this->writeBuffer) {
+            $this->once('drain', array($this, 'closeTransport'));
+            return;
+        }
+
+        $this->closeTransport();
+    }
+
+    public function closeTransport()
+    {
+        //todo onClose.bind(this, 'forced close'));
+        $this->transport->close(array($this, 'onClose'));
+    }
+
+    public function setupSendCallback()
+    {
+        $self = $this;
+        //the message was sent successfully, execute the callback
+        $this->transport->on('drain', array($this, 'onDrainCallback')); 
+    }
+
+    public function onDrainCallback()
+    {
+        if ($this->sentCallbackFn)
+        {
+             $seqFn = array_shift($this->sentCallbackFn);
+             if(is_callable($seqFn))
+             {
+                 echo('executing send callback');
+                 call_user_func($seqFn, $this->transport);
+             }else if (is_array($seqFn)) {
+                 echo('executing batch send callback');
+                 foreach($seqFn as $fn)
+                 {
+                     call_user_func($fn, $this->transport);
+                 }
+            }
+        }
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Transport.php b/vendor/workerman/phpsocket.io/src/Engine/Transport.php
new file mode 100644
index 0000000..0653f35
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Transport.php
@@ -0,0 +1,79 @@
+<?php
+namespace PHPSocketIO\Engine;
+use \PHPSocketIO\Event\Emitter;
+use \PHPSocketIO\Debug;
+class Transport extends Emitter
+{
+    public $readyState = 'opening';
+    public $req = null;
+    public $res = null;
+    
+    public function __construct()
+    {
+        Debug::debug('Transport __construct no access !!!!');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('Transport __destruct');
+    }
+
+    public function noop()
+    {
+ 
+    } 
+
+    public function onRequest($req)
+    {
+        $this->req = $req;
+    }
+    
+    public function close($fn = null)
+    {
+        $this->readyState = 'closing';
+        $fn = $fn ? $fn : array($this, 'noop');
+        $this->doClose($fn);
+    }
+
+    public function onError($msg, $desc = '')
+    {
+        if ($this->listeners('error')) 
+        {
+            $err = array(
+                'type' => 'TransportError',
+                'description' => $desc,
+            );
+            $this->emit('error', $err);
+        } 
+        else 
+        {
+            echo("ignored transport error $msg $desc\n");
+        }
+    }
+
+    public function onPacket($packet)
+    {
+        $this->emit('packet', $packet);
+    }
+
+    public function onData($data)
+    {
+        $this->onPacket(Parser::decodePacket($data));
+    }
+    
+    public function onClose() 
+    {
+        $this->req = $this->res = null;
+        $this->readyState = 'closed';
+        $this->emit('close');
+        $this->removeAllListeners();
+    }
+
+    public function destroy()
+    {
+         $this->req = $this->res = null;
+         $this->readyState = 'closed';
+         $this->removeAllListeners();
+         $this->shouldClose = null;
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Transports/Polling.php b/vendor/workerman/phpsocket.io/src/Engine/Transports/Polling.php
new file mode 100644
index 0000000..8ad0dc5
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Transports/Polling.php
@@ -0,0 +1,208 @@
+<?php
+namespace PHPSocketIO\Engine\Transports;
+use PHPSocketIO\Engine\Transport;
+use PHPSocketIO\Engine\Parser;
+use \PHPSocketIO\Debug;
+class Polling extends Transport
+{
+    public $name = 'polling';
+    public $chunks = '';
+    public $shouldClose = null;
+    public $writable = false;
+    public function onRequest($req)
+    {
+        $res = $req->res;
+
+        if ('GET' === $req->method) 
+        {
+            $this->onPollRequest($req, $res);
+        } 
+        else if('POST' === $req->method) 
+        {
+            $this->onDataRequest($req, $res);
+        } 
+        else 
+        {
+            $res->writeHead(500);
+            $res->end();
+        }
+    }
+
+    public function onPollRequest($req, $res) 
+    {
+        if($this->req) 
+        {
+            echo ('request overlap');
+            // assert: this.res, '.req and .res should be (un)set together'
+            $this->onError('overlap from client');
+            $res->writeHead(500);
+            return;
+        }
+
+        $this->req = $req;
+        $this->res = $res;
+
+
+        $req->onClose = array($this, 'pollRequestOnClose');
+        $req->cleanup = array($this, 'pollRequestClean');
+
+        $this->writable = true;
+        $this->emit('drain');
+
+        // if we're still writable but had a pending close, trigger an empty send
+        if ($this->writable && $this->shouldClose) 
+        {
+            echo('triggering empty send to append close packet');
+            $this->send(array(array('type'=>'noop')));
+        }
+    }
+
+    public function pollRequestOnClose()
+    {
+        $this->onError('poll connection closed prematurely');
+        $this->pollRequestClean();
+    }
+    
+    public function pollRequestClean()
+    {
+        if(isset($this->req))
+        {
+            $this->req->res = null;
+            $this->req->onClose = $this->req->cleanup = null;
+            $this->req = $this->res = null;
+        }
+    }
+
+    public function onDataRequest($req, $res) 
+    {
+        if(isset($this->dataReq)) 
+        {
+            // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
+            $this->onError('data request overlap from client');
+            $res->writeHead(500);
+            return;
+        }
+
+        $this->dataReq = $req;
+        $this->dataRes = $res;
+        $req->onClose = array($this, 'dataRequestOnClose');
+        $req->onData = array($this, 'dataRequestOnData');
+        $req->onEnd = array($this, 'dataRequestOnEnd');
+    }
+
+    public function dataRequestCleanup()
+    {
+        $this->chunks = '';
+        $this->dataReq->res = null;
+        $this->dataReq->onClose = $this->dataReq->onData = $this->dataReq->onEnd = null;
+        $this->dataReq = $this->dataRes = null;
+    }
+
+    public function dataRequestOnClose()
+    {
+        $this->dataRequestCleanup();
+        $this->onError('data request connection closed prematurely');
+    }
+
+    public function dataRequestOnData($req, $data)
+    {
+        $this->chunks .= $data;
+        // todo maxHttpBufferSize
+        /*if(strlen($this->chunks) > $this->maxHttpBufferSize)
+        {
+            $this->chunks = '';
+            $req->connection->destroy();
+        }*/
+    }
+
+    public function dataRequestOnEnd () 
+    {
+        $this->onData($this->chunks);
+
+        $headers = array(
+           'Content-Type'=> 'text/html',
+           'Content-Length'=> 2,
+           'X-XSS-Protection' => '0',
+        );
+
+        $this->dataRes->writeHead(200, '', $this->headers($this->dataReq, $headers));
+        $this->dataRes->end('ok');
+        $this->dataRequestCleanup();
+    }
+    
+    public function onData($data)
+    {
+       $packets = Parser::decodePayload($data);
+       if(isset($packets['type']))
+       {
+           if('close' === $packets['type'])
+           {
+               $this->onClose();
+               return false;
+           }
+           else
+           {
+               $packets = array($packets);
+           }
+       }
+       
+       foreach($packets as $packet)
+       {
+           $this->onPacket($packet);
+       }
+    }
+    
+    public function onClose()
+    {
+       if($this->writable) 
+       {
+           // close pending poll request
+           $this->send(array(array('type'=> 'noop')));
+       }
+       parent::onClose();
+    }
+
+    public function send($packets) 
+    {   
+        $this->writable = false;
+        if($this->shouldClose) 
+        {
+            echo('appending close packet to payload');
+            $packets[] = array('type'=>'close');
+            call_user_func($this->shouldClose);
+            $this->shouldClose = null;
+        }
+        $data = Parser::encodePayload($packets, $this->supportsBinary);
+        $this->write($data);
+    }
+
+    public function write($data) 
+    {
+        $this->doWrite($data);
+        if(!empty($this->req->cleanup))
+        {
+            call_user_func($this->req->cleanup);
+        }
+    }
+
+    public function doClose($fn) 
+    {
+       if(!empty($this->dataReq)) 
+       {
+           //echo('aborting ongoing data request');
+           $this->dataReq->destroy();
+       }
+
+       if($this->writable)
+       {
+           //echo('transport writable - closing right away');
+           $this->send(array(array('type'=> 'close')));
+           call_user_func($fn);
+       }
+       else
+       {
+           //echo("transport not writable - buffering orderly close\n");
+           $this->shouldClose = $fn;
+       }
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Transports/PollingJsonp.php b/vendor/workerman/phpsocket.io/src/Engine/Transports/PollingJsonp.php
new file mode 100644
index 0000000..024c073
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Transports/PollingJsonp.php
@@ -0,0 +1,63 @@
+<?php
+namespace PHPSocketIO\Engine\Transports;
+use \PHPSocketIO\Debug;
+class PollingJsonp extends Polling
+{
+    public $head = null;
+    public $foot = ');';
+
+    public function __construct($req)
+    {
+        $j = isset($req->_query['j']) ? preg_replace('/[^0-9]/', '', $req->_query['j']) : '';
+        $this->head = "___eio[ $j ](";
+        Debug::debug('PollingJsonp __construct');
+    }
+public function __destruct()
+{
+    Debug::debug('PollingJsonp __destruct');
+}
+    public function onData($data)
+    {
+        $parsed_data = null;
+        parse_str($data, $parsed_data);
+        $data = $parsed_data['d'];
+        // todo check
+        //client will send already escaped newlines as \\\\n and newlines as \\n
+        // \\n must be replaced with \n and \\\\n with \\n
+        /*data = data.replace(rSlashes, function(match, slashes) {
+          return slashes ? match : '\n';
+        });*/
+        call_user_func(array($this, 'parent::onData'), preg_replace('/\\\\n/', '\\n', $data));
+    }
+
+    public function doWrite($data)
+    {
+        $js = json_encode($data);
+        //$js = preg_replace(array('/\u2028/', '/\u2029/'), array('\\u2028', '\\u2029'), $js);
+
+        // prepare response
+        $data = $this->head . $js . $this->foot;
+
+        // explicit UTF-8 is required for pages not served under utf
+        $headers = array(
+            'Content-Type'=> 'text/javascript; charset=UTF-8',
+            'Content-Length'=> strlen($data),
+            'X-XSS-Protection'=>'0'
+        );
+        if(empty($this->res)){echo new \Exception('empty $this->res');return;}
+        $this->res->writeHead(200, '',$this->headers($this->req, $headers));
+        $this->res->end($data);
+    }
+    
+    public function headers($req, $headers = array())
+    {
+       $listeners = $this->listeners('headers');
+       foreach($listeners as $listener)
+       {
+           $listener($headers);
+       }
+       return $headers;
+    }
+ 
+
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Transports/PollingXHR.php b/vendor/workerman/phpsocket.io/src/Engine/Transports/PollingXHR.php
new file mode 100644
index 0000000..6148c96
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Transports/PollingXHR.php
@@ -0,0 +1,70 @@
+<?php
+namespace PHPSocketIO\Engine\Transports;
+use \PHPSocketIO\Debug;
+class PollingXHR extends Polling
+{
+    public function __construct()
+    {
+        Debug::debug('PollingXHR __construct');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('PollingXHR __destruct');
+    }
+
+    public function onRequest($req)
+    {
+        if('OPTIONS' === $req->method)
+        {
+            $res = $req->res;
+            $headers = $this->headers($req);
+            $headers['Access-Control-Allow-Headers'] = 'Content-Type';
+            $res->writeHead(200, '', $headers);
+            $res->end();
+        } 
+        else
+        {
+            parent::onRequest($req);
+        }
+    }
+
+    public function doWrite($data)
+    {
+        // explicit UTF-8 is required for pages not served under utf todo
+        //$content_type = $isString
+        //    ? 'text/plain; charset=UTF-8'
+        //    : 'application/octet-stream';
+        $content_type = preg_match('/^\d+:/', $data) ? 'text/plain; charset=UTF-8' : 'application/octet-stream';
+        $content_length = strlen($data);
+        $headers = array( 
+            'Content-Type'=> $content_type,
+            'Content-Length'=> $content_length,
+            'X-XSS-Protection' => '0',
+        );
+        if(empty($this->res)){echo new \Exception('empty this->res');return;}
+        $this->res->writeHead(200, '', $this->headers($this->req, $headers));
+        $this->res->end($data);
+    }
+    
+    public function headers($req, $headers = array())
+    {
+       if(isset($req->headers['origin']))
+       {
+           $headers['Access-Control-Allow-Credentials'] = 'true';
+           $headers['Access-Control-Allow-Origin'] = $req->headers['origin'];
+       } 
+       else
+       {
+           $headers['Access-Control-Allow-Origin'] = '*';
+       }
+       $listeners = $this->listeners('headers');
+       foreach($listeners as $listener)
+       {
+           $listener($headers);
+       }
+       return $headers;
+    }
+ 
+
+}
diff --git a/vendor/workerman/phpsocket.io/src/Engine/Transports/WebSocket.php b/vendor/workerman/phpsocket.io/src/Engine/Transports/WebSocket.php
new file mode 100644
index 0000000..23f4b97
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Engine/Transports/WebSocket.php
@@ -0,0 +1,58 @@
+<?php
+namespace PHPSocketIO\Engine\Transports;
+use \PHPSocketIO\Engine\Transport;
+use \PHPSocketIO\Engine\Parser;
+use \PHPSocketIO\Debug;
+class WebSocket extends Transport
+{
+    public $writable = true;
+    public $supportsFraming = true;
+    public $supportsBinary = true;
+    public $name = 'websocket';
+    public function __construct($req)
+    {
+        $this->socket = $req->connection;
+        $this->socket->onMessage = array($this, 'onData2');
+        $this->socket->onClose = array($this, 'onClose');
+        $this->socket->onError = array($this, 'onError2');
+        Debug::debug('WebSocket __construct');
+    }
+    public function __destruct()
+    {
+        Debug::debug('WebSocket __destruct');
+    }
+    public function onData2($connection, $data) 
+    {
+        call_user_func(array($this, 'parent::onData'), $data);
+    }
+    
+    public function onError2($conection, $code, $msg)
+    {
+        call_user_func(array($this, 'parent::onClose'), $code, $msg);
+    }
+    
+    public function send($packets)
+    {
+        foreach($packets as $packet)
+        {
+            $data = Parser::encodePacket($packet, $this->supportsBinary);
+            if ($this->socket) {
+                $this->socket->send($data);
+                $this->emit('drain');
+            }
+        }
+    }
+    
+    public function doClose($fn = null) 
+    {
+        if($this->socket)
+        {
+            $this->socket->close();
+            $this->socket = null;
+            if(!empty($fn))
+            {
+                call_user_func($fn);
+            }
+        }
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Event/Emitter.php b/vendor/workerman/phpsocket.io/src/Event/Emitter.php
new file mode 100644
index 0000000..54f6e19
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Event/Emitter.php
@@ -0,0 +1,107 @@
+<?php
+namespace PHPSocketIO\Event;
+
+use PHPSocketIO\Debug;
+
+class Emitter
+{
+    public function __construct()
+{
+    Debug::debug('Emitter __construct');
+}
+
+public function __destruct()
+{
+    Debug::debug('Emitter __destruct');
+}
+
+    /**
+     * [event=>[[listener1, once?], [listener2,once?], ..], ..]
+     */
+    protected $_eventListenerMap = array();
+    
+    public function on($event_name, $listener)
+    {
+        $this->emit('newListener', $event_name, $listener);
+        $this->_eventListenerMap[$event_name][] = array($listener, 0); 
+        return $this;
+    }
+
+    public function once($event_name, $listener)
+    {
+        $this->_eventListenerMap[$event_name][] = array($listener, 1);
+        return $this;
+    }
+   
+    public function removeListener($event_name, $listener)
+    {
+        if(!isset($this->_eventListenerMap[$event_name]))
+        {
+            return $this;
+        }
+        foreach($this->_eventListenerMap[$event_name] as $key=>$item)
+        {
+            if($item[0] === $listener)
+            {
+                $this->emit('removeListener', $event_name, $listener);
+                unset($this->_eventListenerMap[$event_name][$key]);
+            }
+        }
+        if(empty($this->_eventListenerMap[$event_name]))
+        {
+            unset($this->_eventListenerMap[$event_name]);
+        }
+        return $this;
+    }
+
+    public function removeAllListeners($event_name = null)
+    {
+        $this->emit('removeListener', $event_name);
+        if(null === $event_name)
+        {
+            $this->_eventListenerMap = array();
+            return $this;
+        }
+        unset($this->_eventListenerMap[$event_name]);
+        return $this;
+    }
+
+    public function listeners($event_name)
+    {
+        if(empty($this->_eventListenerMap[$event_name]))
+        {
+            return array();
+        }
+        $listeners = array();
+        foreach($this->_eventListenerMap[$event_name] as $item)
+        {
+            $listeners[] = $item[0];
+        }
+        return $listeners;
+    }
+
+    public function emit($event_name = null)
+    {
+        if(empty($event_name) || empty($this->_eventListenerMap[$event_name]))
+        {
+            return false;
+        }
+        foreach($this->_eventListenerMap[$event_name] as $key=>$item)
+        {
+             $args = func_get_args();
+             unset($args[0]);
+             call_user_func_array($item[0], $args);
+             // once ?
+             if($item[1])
+             {
+                 unset($this->_eventListenerMap[$event_name][$key]);
+                 if(empty($this->_eventListenerMap[$event_name]))
+                 {
+                     unset($this->_eventListenerMap[$event_name]);
+                 }
+             }
+        }
+        return true;
+    }
+
+}
diff --git a/vendor/workerman/phpsocket.io/src/Nsp.php b/vendor/workerman/phpsocket.io/src/Nsp.php
new file mode 100644
index 0000000..bc5f3a9
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Nsp.php
@@ -0,0 +1,163 @@
+<?php
+namespace PHPSocketIO;
+use PHPSocketIO\Event\Emitter;
+use PHPSocketIO\Parser\Parser;
+class Nsp extends Emitter
+{
+    public $name = null;
+    public $server = null;
+    public $rooms = array();
+    public $flags = array();
+    public $sockets = array();
+    public $connected = array();
+    public $fns = array();
+    public $ids = 0;
+    public $acks = array();
+    public static $events = array(
+        'connect' => 'connect',    // for symmetry with client
+        'connection' => 'connection',
+        'newListener' => 'newListener'
+    );
+
+    //public static $flags = array('json','volatile');
+
+    public function __construct($server, $name)
+    {
+         $this->name = $name;
+         $this->server = $server;
+         $this->initAdapter();
+         Debug::debug('Nsp __construct');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('Nsp __destruct');
+    }
+
+    public function initAdapter()
+    {
+        $adapter_name = $this->server->adapter();
+        $this->adapter = new $adapter_name($this);
+    }
+
+    public function to($name)
+    {
+        if(!isset($this->rooms[$name]))
+        {
+            $this->rooms[$name] = $name;
+        }
+        return $this;
+    }
+
+    public function in($name)
+    {
+        return $this->to($name);
+    }
+
+
+    public function add($client, $nsp, $fn)
+    {
+        $socket_name = $this->server->socket();
+        $socket = new $socket_name($this, $client);
+        if('open' === $client->conn->readyState)
+        {
+            $this->sockets[$socket->id]=$socket;
+            $socket->onconnect();
+            if(!empty($fn)) call_user_func($fn, $socket, $nsp);
+            $this->emit('connect', $socket);
+            $this->emit('connection', $socket);
+        }
+        else
+        {
+            echo('next called after client was closed - ignoring socket');
+        }
+    }
+
+
+    /**
+     * Removes a client. Called by each `Socket`.
+     *
+     * @api private
+     */
+
+    public function remove($socket)
+    {
+        // todo $socket->id
+        unset($this->sockets[$socket->id]);
+    }
+
+
+    /**
+     * Emits to all clients.
+     *
+     * @return {Namespace} self
+     * @api public
+     */
+
+    public function emit($ev = null)
+    {
+        $args = func_get_args();
+        if (isset(self::$events[$ev]))
+        {
+            call_user_func_array(array(__CLASS__, 'parent::emit'), $args);
+        }
+        else
+        {
+            // set up packet object
+
+            $parserType = Parser::EVENT; // default
+            //if (self::hasBin($args)) { $parserType = Parser::BINARY_EVENT; } // binary
+
+            $packet = array('type'=> $parserType, 'data'=> $args );
+
+            if (is_callable(end($args)))
+            {
+                echo('Callbacks are not supported when broadcasting');
+                return;
+             }
+
+             $this->adapter->broadcast($packet, array(
+                 'rooms'=> $this->rooms,
+                 'flags'=> $this->flags
+             ));
+
+            $this->rooms = array();
+            $this->flags = array();;
+        }
+        return $this;
+    }
+
+    public function send()
+    {
+        $args = func_get_args();
+        array_unshift($args, 'message');
+        $this->emit($args);
+        return $this;
+    }
+
+    public function write()
+    {
+        $args = func_get_args();
+        return call_user_func_array(array($this, 'send'), $args);
+    }
+
+    public function clients($fn)
+    {
+        $this->adapter->clients($this->rooms, $fn);
+        return $this;
+    }
+
+    /**
+     * Sets the compress flag.
+     *
+     * @param {Boolean} if `true`, compresses the sending data
+     * @return {Socket} self
+     * @api public
+     */
+
+     public function compress($compress)
+     {
+        $this->flags['compress'] = $compress;
+        return $this;
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Parser/Decoder.php b/vendor/workerman/phpsocket.io/src/Parser/Decoder.php
new file mode 100644
index 0000000..06a4109
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Parser/Decoder.php
@@ -0,0 +1,140 @@
+<?php
+namespace PHPSocketIO\Parser;
+use \PHPSocketIO\Parser\Parser;
+use \PHPSocketIO\Event\Emitter;
+use \PHPSocketIO\Debug;
+class Decoder extends Emitter 
+{
+    public function __construct()
+    {
+        Debug::debug('Decoder __construct');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('Decoder __destruct');
+    }
+
+    public function add($obj)
+    {
+      if (is_string($obj))
+      {
+          $packet = self::decodeString($obj);
+          if(Parser::BINARY_EVENT == $packet['type'] || Parser::BINARY_ACK == $packet['type'])
+          { 
+               // binary packet's json todo BinaryReconstructor
+               $this->reconstructor = new BinaryReconstructor(packet);
+
+               // no attachments, labeled binary but no binary data to follow
+               if ($this->reconstructor->reconPack->attachments === 0) 
+               {
+                   $this->emit('decoded', $packet);
+               }
+           } else { // non-binary full packet
+               $this->emit('decoded', $packet);
+           }
+       }
+       else if (isBuf($obj) || !empty($obj['base64']))
+       { // raw binary data
+            if (!$this->reconstructor) 
+            {
+                throw new \Exception('got binary data when not reconstructing a packet');
+            } else {
+                $packet = $this->reconstructor->takeBinaryData($obj);
+                if ($packet)
+                { // received final buffer
+                    $this->reconstructor = null;
+                    $this->emit('decoded', $packet);
+                }
+            }
+        }
+        else {
+            throw new \Exception('Unknown type: ' + obj);
+        }
+    }
+
+    public function decodeString($str) 
+    {
+        $p = array();
+        $i = 0;
+
+        // look up type
+        $p['type'] = $str[0];
+        if(!isset(Parser::$types[$p['type']])) return self::error();
+
+        // look up attachments if type binary
+        if(Parser::BINARY_EVENT == $p['type'] || Parser::BINARY_ACK == $p['type'])
+        {
+            $buf = '';
+            while ($str[++$i] != '-')
+            {
+                $buf .= $str[$i];
+                if($i == strlen(str)) break;
+            }
+            if ($buf != intval($buf) || $str[$i] != '-')
+            {
+                throw new \Exception('Illegal attachments');
+            }
+            $p['attachments'] = intval($buf);
+        }
+
+        // look up namespace (if any)
+        if(isset($str[$i + 1]) && '/' === $str[$i + 1])
+        {
+            $p['nsp'] = '';
+            while (++$i)
+            {
+                if ($i === strlen($str)) break;
+                $c = $str[$i];
+                if (',' === $c) break;
+                $p['nsp'] .= $c;
+            }
+        } else {
+            $p['nsp'] = '/';
+        }
+
+        // look up id
+        if(isset($str[$i+1]))
+        {
+            $next = $str[$i+1];
+            if ('' !== $next && strval((int)$next) === strval($next))
+            {
+                $p['id'] = '';
+                while (++$i)
+                {
+                    $c = $str[$i];
+                    if (null == $c || strval((int)$c) != strval($c))
+                    {
+                        --$i;
+                        break;
+                    }
+                    $p['id'] .= $str[$i];
+                    if($i == strlen($str)) break;
+                }
+                $p['id'] = (int)$p['id'];
+            }
+        }
+
+        // look up json data
+        if (isset($str[++$i]))
+        {
+            // todo try
+            $p['data'] = json_decode(substr($str, $i), true);
+        }
+
+        return $p;
+    }
+    
+    public static function error()
+    {
+         return array(
+            'type'=> Parser::ERROR,
+            'data'=> 'parser error'
+         );
+    }
+
+    public function destroy()
+    {
+
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/Parser/Encoder.php b/vendor/workerman/phpsocket.io/src/Parser/Encoder.php
new file mode 100644
index 0000000..87a57fe
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Parser/Encoder.php
@@ -0,0 +1,76 @@
+<?php
+namespace PHPSocketIO\Parser;
+use \PHPSocketIO\Parser\Parser;
+use \PHPSocketIO\Event\Emitter;
+use \PHPSocketIO\Debug;
+class Encoder extends Emitter 
+{
+    public function __construct()
+    {
+        Debug::debug('Encoder __construct');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('Encoder __destruct');
+    }
+
+
+    public function encode($obj)
+    {
+        if(Parser::BINARY_EVENT == $obj['type'] || Parser::BINARY_ACK == $obj['type']) 
+        {
+            echo new \Exception("not support BINARY_EVENT BINARY_ACK");
+            return array(); 
+        }
+        else 
+        {
+            $encoding = self::encodeAsString($obj);
+            return array($encoding);
+        }
+    }
+
+    public static function encodeAsString($obj) {
+        $str = '';
+        $nsp = false;
+
+        // first is type
+        $str .= $obj['type'];
+
+        // attachments if we have them
+        if (Parser::BINARY_EVENT == $obj['type'] || Parser::BINARY_ACK == $obj['type']) 
+        {
+            $str .= $obj['attachments'];
+            $str .= '-';
+        }
+
+        // if we have a namespace other than `/`
+        // we append it followed by a comma `,`
+        if (!empty($obj['nsp']) && '/' !== $obj['nsp']) 
+        {
+            $nsp = true;
+            $str .= $obj['nsp'];
+        }
+
+        // immediately followed by the id
+        if (isset($obj['id'])) 
+        {
+            if($nsp)
+            {
+                $str .= ',';
+                $nsp = false;
+            }
+            $str .= $obj['id'];
+        }
+
+        // json data
+        if(isset($obj['data']))
+        {
+            if ($nsp) $str .= ',';
+            $str .= json_encode($obj['data']);
+        }
+
+        return $str;
+    }
+
+}
diff --git a/vendor/workerman/phpsocket.io/src/Parser/Parser.php b/vendor/workerman/phpsocket.io/src/Parser/Parser.php
new file mode 100644
index 0000000..2d46f08
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Parser/Parser.php
@@ -0,0 +1,63 @@
+<?php
+namespace PHPSocketIO\Parser;
+class Parser 
+{
+/**
+ * Packet type `connect`.
+ *
+ * @api public
+ */
+    const CONNECT = 0;
+
+/**
+ * Packet type `disconnect`.
+ *
+ * @api public
+ */
+    const DISCONNECT = 1;
+
+/**
+ * Packet type `event`.
+ *
+ * @api public
+ */
+    const EVENT = 2;
+
+/**
+ * Packet type `ack`.
+ *
+ * @api public
+ */
+    const ACK = 3;
+
+/**
+ * Packet type `error`.
+ *
+ * @api public
+ */
+    const ERROR = 4;
+
+/**
+ * Packet type 'binary event'
+ *
+ * @api public
+ */
+    const BINARY_EVENT = 5;
+
+/**
+ * Packet type `binary ack`. For acks with binary arguments.
+ *
+ * @api public
+ */
+    const BINARY_ACK = 6;
+ 
+    public static $types = array(
+      'CONNECT',
+      'DISCONNECT',
+      'EVENT',
+      'BINARY_EVENT',
+      'ACK',
+      'BINARY_ACK',
+      'ERROR'
+    );
+}
diff --git a/vendor/workerman/phpsocket.io/src/Socket.php b/vendor/workerman/phpsocket.io/src/Socket.php
new file mode 100644
index 0000000..a266875
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/Socket.php
@@ -0,0 +1,472 @@
+<?php
+namespace PHPSocketIO;
+use PHPSocketIO\Event\Emitter;
+use PHPSocketIO\Parser\Parser;
+class Socket extends Emitter
+{
+    public $nsp = null;
+    public $server = null;
+    public $adapter = null;
+    public $id = null;
+    public $path = '/';
+    public $request = null;
+    public $client = null;
+    public $conn = null;
+    public $rooms = array();
+    public $_rooms = array();
+    public $flags = array();
+    public $acks = array();
+    public $connected = true;
+    public $disconnected = false;
+
+    public static $events = array(
+        'error'=>'error',
+        'connect' => 'connect',
+        'disconnect' => 'disconnect',
+        'newListener' => 'newListener',
+        'removeListener' => 'removeListener'
+    );
+
+    public static $flagsMap = array(
+        'json' => 'json',
+        'volatile' => 'volatile',
+        'broadcast' => 'broadcast'
+    );
+
+    public function __construct($nsp, $client)
+    {
+        $this->nsp = $nsp;
+        $this->server = $nsp->server;
+        $this->adapter = $this->nsp->adapter;
+        $this->id = ($nsp->name !== '/') ? $nsp->name .'#' .$client->id : $client->id;
+        $this->request = $client->request;
+        $this->client = $client;
+        $this->conn = $client->conn;
+        $this->handshake = $this->buildHandshake();
+        Debug::debug('IO Socket __construct');
+    }
+
+    public function __destruct()
+    {
+        Debug::debug('IO Socket __destruct');
+    }
+
+    public function buildHandshake()
+    {
+        //todo check this->request->_query
+        $info = !empty($this->request->url) ?  parse_url($this->request->url) : array();
+        $query = array();
+        if(isset($info['query']))
+        {
+            parse_str($info['query'], $query);
+        }
+        return array(
+            'headers' => isset($this->request->headers) ? $this->request->headers : array(),
+            'time'=> date('D M d Y H:i:s') . ' GMT',
+            'address'=> $this->conn->remoteAddress,
+            'xdomain'=> isset($this->request->headers['origin']),
+            'secure' => !empty($this->request->connection->encrypted),
+            'issued' => time(),
+            'url' => isset($this->request->url) ? $this->request->url : '',
+            'query' => $query,
+       );
+    }
+
+    public function __get($name)
+    {
+        if($name === 'broadcast')
+        {
+            $this->flags['broadcast'] = true;
+            return $this;
+        }
+        return null;
+    }
+
+    public function emit($ev = null)
+    {
+        $args = func_get_args();
+        if (isset(self::$events[$ev]))
+        {
+            call_user_func_array(array(__CLASS__, 'parent::emit'), $args);
+        }
+        else
+        {
+            $packet = array();
+            // todo check
+            //$packet['type'] = hasBin($args) ? Parser::BINARY_EVENT : Parser::EVENT;
+            $packet['type'] = Parser::EVENT;
+            $packet['data'] = $args;
+            $flags = $this->flags;
+            // access last argument to see if it's an ACK callback
+            if (is_callable(end($args)))
+            {
+                if ($this->_rooms || isset($flags['broadcast']))
+                {
+                    throw new \Exception('Callbacks are not supported when broadcasting');
+                }
+                echo('emitting packet with ack id ' . $this->nsp->ids);
+                $this->acks[$this->nsp->ids] = array_pop($args);
+                $packet['id'] = $this->nsp->ids++;
+            }
+
+            if ($this->_rooms || !empty($flags['broadcast']))
+            {
+                $this->adapter->broadcast($packet, array(
+                    'except' => array($this->id => $this->id),
+                    'rooms'=> $this->_rooms,
+                    'flags' => $flags
+                ));
+            }
+            else
+            {
+                // dispatch packet
+                $this->packet($packet);
+            }
+
+            // reset flags
+            $this->_rooms = array();
+            $this->flags = array();
+        }
+        return $this;
+    }
+
+
+    /**
+     * Targets a room when broadcasting.
+     *
+     * @param {String} name
+     * @return {Socket} self
+     * @api public
+     */
+
+    public function to($name)
+    {
+        if(!isset($this->_rooms[$name]))
+        {
+            $this->_rooms[$name] = $name;
+        }
+        return $this;
+    }
+
+    public function in($name)
+    {
+        return $this->to($name);
+    }
+
+    /**
+     * Sends a `message` event.
+     *
+     * @return {Socket} self
+     * @api public
+     */
+
+    public function send()
+    {
+        $args = func_get_args();
+        array_unshift($args, 'message');
+        call_user_func_array(array($this, 'emit'), $args);
+        return $this;
+    }
+
+    public function write()
+    {
+        $args = func_get_args();
+        array_unshift($args, 'message');
+        call_user_func_array(array($this, 'emit'), $args);
+        return $this;
+    }
+
+    /**
+     * Writes a packet.
+     *
+     * @param {Object} packet object
+     * @param {Object} options
+     * @api private
+     */
+
+    public function packet($packet, $preEncoded = false)
+    {
+        if (!$this->nsp || !$this->client) return;
+        $packet['nsp'] = $this->nsp->name;
+        //$volatile = !empty(self::$flagsMap['volatile']);
+        $volatile = false;
+        $this->client->packet($packet, $preEncoded, $volatile);
+    }
+
+    /**
+     * Joins a room.
+     *
+     * @param {String} room
+     * @param {Function} optional, callback
+     * @return {Socket} self
+     * @api private
+     */
+
+     public function join($room)
+     {
+        if(isset($this->rooms[$room])) return $this;
+        $this->adapter->add($this->id, $room);
+        $this->rooms[$room] = $room;
+        return $this;
+    }
+
+    /**
+     * Leaves a room.
+     *
+     * @param {String} room
+     * @param {Function} optional, callback
+     * @return {Socket} self
+     * @api private
+     */
+
+    public function leave($room)
+    {
+        $this->adapter->del($this->id, $room);
+        unset($this->rooms[$room]);
+        return $this;
+    }
+
+    /**
+     * Leave all rooms.
+     *
+     * @api private
+     */
+
+    public function leaveAll()
+    {
+        $this->adapter->delAll($this->id);
+        $this->rooms = array();
+    }
+
+    /**
+     * Called by `Namespace` upon succesful
+     * middleware execution (ie: authorization).
+     *
+     * @api private
+     */
+
+    public function onconnect()
+    {
+        $this->nsp->connected[$this->id] = $this;
+        $this->join($this->id);
+        $this->packet(array(
+            'type' => Parser::CONNECT)
+         );
+    }
+
+    /**
+     * Called with each packet. Called by `Client`.
+     *
+     * @param {Object} packet
+     * @api private
+     */
+
+    public function onpacket($packet)
+    {
+        switch ($packet['type'])
+        {
+            case Parser::EVENT:
+                $this->onevent($packet);
+                break;
+
+            case Parser::BINARY_EVENT:
+                $this->onevent($packet);
+                break;
+
+            case Parser::ACK:
+                $this->onack($packet);
+                break;
+
+            case Parser::BINARY_ACK:
+                $this->onack($packet);
+                break;
+
+            case Parser::DISCONNECT:
+                $this->ondisconnect();
+                break;
+
+            case Parser::ERROR:
+                $this->emit('error', $packet['data']);
+        }
+    }
+
+    /**
+     * Called upon event packet.
+     *
+     * @param {Object} packet object
+     * @api private
+     */
+
+    public function onevent($packet)
+    {
+        $args = isset($packet['data']) ? $packet['data'] : array();
+        if (!empty($packet['id']) || (isset($packet['id']) && $packet['id'] === 0))
+        {
+            $args[] = $this->ack($packet['id']);
+        }
+        call_user_func_array(array(__CLASS__, 'parent::emit'), $args);
+    }
+
+    /**
+     * Produces an ack callback to emit with an event.
+     *
+     * @param {Number} packet id
+     * @api private
+     */
+
+    public function ack($id)
+    {
+        $self = $this;
+        $sent = false;
+        return function()use(&$sent, $id, $self){
+            // prevent double callbacks
+            if ($sent) return;
+            $args = func_get_args();
+            $type = $this->hasBin($args) ? Parser::BINARY_ACK : Parser::ACK;
+            $self->packet(array(
+                'id' => $id,
+                'type' => $type,
+                'data' => $args
+            ));
+        };
+    }
+
+    /**
+     * Called upon ack packet.
+     *
+     * @api private
+     */
+
+    public function onack($packet)
+    {
+        $ack = $this->acks[$packet['id']];
+        if (is_callable($ack))
+        {
+            call_user_func($ack, $packet['data']);
+            unset($this->acks[$packet['id']]);
+        } else {
+            echo ('bad ack '. packet.id);
+        }
+    }
+
+    /**
+     * Called upon client disconnect packet.
+     *
+     * @api private
+     */
+
+    public function ondisconnect()
+    {
+        //echo('got disconnect packet');
+        $this->onclose('client namespace disconnect');
+    }
+
+    /**
+     * Handles a client error.
+     *
+     * @api private
+     */
+
+    public function onerror($err)
+    {
+        if ($this->listeners('error'))
+        {
+            $this->emit('error', $err);
+        }
+        else
+        {
+            //echo('Missing error handler on `socket`.');
+        }
+    }
+
+    /**
+     * Called upon closing. Called by `Client`.
+     *
+     * @param {String} reason
+     * @param {Error} optional error object
+     * @api private
+     */
+
+     public function onclose($reason)
+     {
+        if (!$this->connected) return $this;
+        $this->emit('disconnect', $reason);
+        $this->leaveAll();
+        $this->nsp->remove($this);
+        $this->client->remove($this);
+        $this->connected = false;
+        $this->disconnected = true;
+        unset($this->nsp->connected[$this->id]);
+        // ....
+        $this->nsp = null;
+        $this->server = null;
+        $this->adapter = null;
+        $this->request = null;
+        $this->client = null;
+        $this->conn = null;
+        $this->removeAllListeners();
+    }
+
+    /**
+     * Produces an `error` packet.
+     *
+     * @param {Object} error object
+     * @api private
+     */
+
+    public function error($err)
+    {
+        $this->packet(array(
+            'type' => Parser::ERROR, 'data' => $err )
+         );
+    }
+
+    /**
+     * Disconnects this client.
+     *
+     * @param {Boolean} if `true`, closes the underlying connection
+     * @return {Socket} self
+     * @api public
+     */
+
+    public function disconnect( $close = false )
+    {
+        if (!$this->connected) return $this;
+        if ($close)
+        {
+            $this->client->disconnect();
+        } else {
+            $this->packet(array(
+                'type'=> Parser::DISCONNECT
+            ));
+            $this->onclose('server namespace disconnect');
+        }
+        return $this;
+    }
+
+    /**
+     * Sets the compress flag.
+     *
+     * @param {Boolean} if `true`, compresses the sending data
+     * @return {Socket} self
+     * @api public
+     */
+
+    public function compress($compress)
+    {
+        $this->flags['compress'] = $compress;
+        return $this;
+    }
+
+    protected function hasBin($args) {
+        $hasBin = false;
+
+        array_walk_recursive($args, function($item, $key) use ($hasBin) {
+            if (!ctype_print($item)) {
+                $hasBin = true;
+            }
+        });
+
+        return $hasBin;
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/SocketIO.php b/vendor/workerman/phpsocket.io/src/SocketIO.php
new file mode 100644
index 0000000..d7489e5
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/SocketIO.php
@@ -0,0 +1,171 @@
+<?php
+namespace PHPSocketIO;
+use Workerman\Worker;
+use PHPSocketIO\Engine\Engine;
+class SocketIO
+{
+    public $nsps = array();
+    protected $_nsp = null;
+    protected $_socket = null;
+    protected $_adapter = null;
+    public $eio = null;
+    public $engine = null;
+    protected $_origins = '*:*';
+    protected $_path = null;
+
+    public function __construct($port = null, $opts = array())
+    {
+        $nsp = isset($opts['nsp']) ? $opts['nsp'] : '\PHPSocketIO\Nsp';
+        $this->nsp($nsp);
+
+        $socket = isset($opts['socket']) ? $opts['socket'] : '\PHPSocketIO\Socket';
+        $this->socket($socket);
+
+        $adapter = isset($opts['adapter']) ? $opts['adapter'] : '\PHPSocketIO\DefaultAdapter';
+        $this->adapter($adapter);
+        if(isset($opts['origins']))
+        {
+            $this->origins($opts['origins']);
+        }
+
+        unset($opts['nsp'], $opts['socket'], $opts['adapter'], $opts['origins']);
+
+        $this->sockets = $this->of('/');
+
+        if(!class_exists('Protocols\SocketIO'))
+        {
+            class_alias('PHPSocketIO\Engine\Protocols\SocketIO', 'Protocols\SocketIO');
+        }
+        if($port)
+        {
+            $worker = new Worker('SocketIO://0.0.0.0:'.$port, $opts);
+            $worker->name = 'PHPSocketIO';
+
+            if(isset($opts['ssl'])) {
+                $worker->transport = 'ssl';
+            }
+
+            $this->attach($worker);
+        }
+    }
+
+    public function nsp($v = null)
+    {
+         if (empty($v)) return $this->_nsp;
+         $this->_nsp = $v;
+         return $this;
+    }
+
+    public function socket($v = null)
+    {
+         if (empty($v)) return $this->_socket;
+         $this->_socket = $v;
+         return $this;
+    }
+
+    public function adapter($v = null)
+    {
+         if (empty($v)) return $this->_adapter;
+         $this->_adapter = $v;
+         foreach($this->nsps as $nsp)
+         {
+             $nsp->initAdapter();
+         }
+         return $this;
+    }
+
+    public function origins($v = null)
+    {
+        if ($v === null) return $this->_origins;
+        $this->_origins = $v;
+        if(isset($this->engine)) {
+            $this->engine->origins = $this->_origins;
+        }
+        return $this;
+    }
+
+    public function attach($srv, $opts = array())
+    {
+         $engine = new Engine();
+         $this->eio = $engine->attach($srv, $opts);
+
+         // Export http server
+         $this->worker = $srv;
+
+         // bind to engine events
+         $this->bind($engine);
+
+         return $this;
+    }
+
+    public function bind($engine)
+    {
+        $this->engine = $engine;
+        $this->engine->on('connection', array($this, 'onConnection'));
+        $this->engine->origins = $this->_origins;
+        return $this;
+    }
+
+    public function of($name, $fn = null)
+    {
+        if($name[0] !== '/')
+        {
+            $name = "/$name";
+        }
+        if(empty($this->nsps[$name]))
+        {
+            $nsp_name = $this->nsp();
+            $this->nsps[$name] = new $nsp_name($this, $name);
+        }
+        if ($fn)
+        {
+            $this->nsps[$name]->on('connect', $fn);
+        }
+        return $this->nsps[$name];
+    }
+
+    public function onConnection($engine_socket)
+    {
+        $client = new Client($this, $engine_socket);
+        $client->connect('/');
+        return $this;
+    }
+
+    public function on()
+    {
+        $args = array_pad(func_get_args(), 2, null);
+
+        if ($args[0] === 'workerStart') {
+           $this->worker->onWorkerStart = $args[1];
+        } else if ($args[0] === 'workerStop') {
+           $this->worker->onWorkerStop = $args[1];
+        } else if ($args[0] !== null) {
+            return call_user_func_array(array($this->sockets, 'on'), $args);
+        }
+    }
+
+    public function in()
+    {
+        return call_user_func_array(array($this->sockets, 'in'), func_get_args());
+    }
+
+    public function to()
+    {
+        return call_user_func_array(array($this->sockets, 'to'), func_get_args());
+    }
+
+    public function emit()
+    {
+        return call_user_func_array(array($this->sockets, 'emit'), func_get_args());
+    }
+
+    public function send()
+    {
+        return call_user_func_array(array($this->sockets, 'send'), func_get_args());
+    }
+
+    public function write()
+    {
+        return call_user_func_array(array($this->sockets, 'write'), func_get_args());
+    }
+}
diff --git a/vendor/workerman/phpsocket.io/src/autoload.php b/vendor/workerman/phpsocket.io/src/autoload.php
new file mode 100644
index 0000000..4e656fc
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/src/autoload.php
@@ -0,0 +1,15 @@
+<?php
+spl_autoload_register(function($name){
+    $path = str_replace('\\', DIRECTORY_SEPARATOR ,$name);
+    $path = str_replace('PHPSocketIO', '', $path);
+    if(is_file($class_file = __DIR__ . "/$path.php"))
+    {
+        require_once($class_file);
+        if(class_exists($name, false))
+        {
+            return true;
+        }
+    }
+    return false;
+});
+
diff --git a/vendor/workerman/phpsocket.io/tests/emitter.php b/vendor/workerman/phpsocket.io/tests/emitter.php
new file mode 100644
index 0000000..23b9f9f
--- /dev/null
+++ b/vendor/workerman/phpsocket.io/tests/emitter.php
@@ -0,0 +1,28 @@
+<?php
+$rootPath = join(DIRECTORY_SEPARATOR, array(__DIR__,".."));
+include join(DIRECTORY_SEPARATOR, array($rootPath,"vendor","autoload.php"));
+include join(DIRECTORY_SEPARATOR, array($rootPath,"src","Event","Emitter.php"));
+
+ini_set('display_errors', 'on');
+$emitter = new PHPSocketIO\Event\Emitter;
+$func = function($arg1, $arg2)
+{
+    var_dump($arg1, $arg2);
+};
+$emitter->on('removeListener', function($event_name, $func){echo $event_name,':',var_export($func, true),"removed\n";});
+$emitter->on('newListener', function($event_name, $func){echo $event_name,':',var_export($func, true)," added\n";});
+$emitter->on('test', $func);
+$emitter->on('test', $func);
+$emitter->emit('test', 1 ,2);
+echo "----------------------\n";
+$emitter->once('test', $func);
+$emitter->emit('test', 3 ,4);
+echo "----------------------\n";
+$emitter->emit('test', 4 ,4);
+echo "----------------------\n";
+$emitter->removeListener('test', $func)->emit('test', 5 ,6);
+echo "----------------------\n";
+$emitter->on('test2', function(){echo "test2\n";});
+
+var_dump($emitter->listeners('test2'));
+
diff --git a/vendor/workerman/workerman/.github/FUNDING.yml b/vendor/workerman/workerman/.github/FUNDING.yml
new file mode 100644
index 0000000..beae44f
--- /dev/null
+++ b/vendor/workerman/workerman/.github/FUNDING.yml
@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+
+open_collective: workerman
+patreon: walkor
diff --git a/vendor/workerman/workerman/.gitignore b/vendor/workerman/workerman/.gitignore
new file mode 100644
index 0000000..f3f9e18
--- /dev/null
+++ b/vendor/workerman/workerman/.gitignore
@@ -0,0 +1,6 @@
+logs
+.buildpath
+.project
+.settings
+.idea
+.DS_Store
diff --git a/vendor/workerman/workerman/Autoloader.php b/vendor/workerman/workerman/Autoloader.php
new file mode 100644
index 0000000..7d760e9
--- /dev/null
+++ b/vendor/workerman/workerman/Autoloader.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman;
+
+/**
+ * Autoload.
+ */
+class Autoloader
+{
+    /**
+     * Autoload root path.
+     *
+     * @var string
+     */
+    protected static $_autoloadRootPath = '';
+
+    /**
+     * Set autoload root path.
+     *
+     * @param string $root_path
+     * @return void
+     */
+    public static function setRootPath($root_path)
+    {
+        self::$_autoloadRootPath = $root_path;
+    }
+
+    /**
+     * Load files by namespace.
+     *
+     * @param string $name
+     * @return boolean
+     */
+    public static function loadByNamespace($name)
+    {
+        $class_path = \str_replace('\\', \DIRECTORY_SEPARATOR, $name);
+        if (\strpos($name, 'Workerman\\') === 0) {
+            $class_file = __DIR__ . \substr($class_path, \strlen('Workerman')) . '.php';
+        } else {
+            if (self::$_autoloadRootPath) {
+                $class_file = self::$_autoloadRootPath . \DIRECTORY_SEPARATOR . $class_path . '.php';
+            }
+            if (empty($class_file) || !\is_file($class_file)) {
+                $class_file = __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . "$class_path.php";
+            }
+        }
+
+        if (\is_file($class_file)) {
+            require_once($class_file);
+            if (\class_exists($name, false)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+
+\spl_autoload_register('\Workerman\Autoloader::loadByNamespace');
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Connection/AsyncTcpConnection.php b/vendor/workerman/workerman/Connection/AsyncTcpConnection.php
new file mode 100644
index 0000000..600d700
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/AsyncTcpConnection.php
@@ -0,0 +1,376 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+use Workerman\Events\EventInterface;
+use Workerman\Lib\Timer;
+use Workerman\Worker;
+use \Exception;
+
+/**
+ * AsyncTcpConnection.
+ */
+class AsyncTcpConnection extends TcpConnection
+{
+    /**
+     * Emitted when socket connection is successfully established.
+     *
+     * @var callable|null
+     */
+    public $onConnect = null;
+
+    /**
+     * Transport layer protocol.
+     *
+     * @var string
+     */
+    public $transport = 'tcp';
+
+    /**
+     * Status.
+     *
+     * @var int
+     */
+    protected $_status = self::STATUS_INITIAL;
+
+    /**
+     * Remote host.
+     *
+     * @var string
+     */
+    protected $_remoteHost = '';
+
+    /**
+     * Remote port.
+     *
+     * @var int
+     */
+    protected $_remotePort = 80;
+
+    /**
+     * Connect start time.
+     *
+     * @var float
+     */
+    protected $_connectStartTime = 0;
+
+    /**
+     * Remote URI.
+     *
+     * @var string
+     */
+    protected $_remoteURI = '';
+
+    /**
+     * Context option.
+     *
+     * @var array
+     */
+    protected $_contextOption = null;
+
+    /**
+     * Reconnect timer.
+     *
+     * @var int
+     */
+    protected $_reconnectTimer = null;
+
+
+    /**
+     * PHP built-in protocols.
+     *
+     * @var array
+     */
+    protected static $_builtinTransports = array(
+        'tcp'   => 'tcp',
+        'udp'   => 'udp',
+        'unix'  => 'unix',
+        'ssl'   => 'ssl',
+        'sslv2' => 'sslv2',
+        'sslv3' => 'sslv3',
+        'tls'   => 'tls'
+    );
+
+    /**
+     * Construct.
+     *
+     * @param string $remote_address
+     * @param array $context_option
+     * @throws Exception
+     */
+    public function __construct($remote_address, array $context_option = array())
+    {
+        $address_info = \parse_url($remote_address);
+        if (!$address_info) {
+            list($scheme, $this->_remoteAddress) = \explode(':', $remote_address, 2);
+            if('unix' === strtolower($scheme)) { 
+                $this->_remoteAddress = substr($remote_address, strpos($remote_address, '/') + 2);
+            }
+            if (!$this->_remoteAddress) {
+                Worker::safeEcho(new \Exception('bad remote_address'));
+            }
+        } else {
+            if (!isset($address_info['port'])) {
+                $address_info['port'] = 0;
+            }
+            if (!isset($address_info['path'])) {
+                $address_info['path'] = '/';
+            }
+            if (!isset($address_info['query'])) {
+                $address_info['query'] = '';
+            } else {
+                $address_info['query'] = '?' . $address_info['query'];
+            }
+            $this->_remoteHost    = $address_info['host'];
+            $this->_remotePort    = $address_info['port'];
+            $this->_remoteURI     = "{$address_info['path']}{$address_info['query']}";
+            $scheme               = isset($address_info['scheme']) ? $address_info['scheme'] : 'tcp';
+            $this->_remoteAddress = 'unix' === strtolower($scheme) 
+                                    ? substr($remote_address, strpos($remote_address, '/') + 2)
+                                    : $this->_remoteHost . ':' . $this->_remotePort;
+        }
+
+        $this->id = $this->_id = self::$_idRecorder++;
+        if(\PHP_INT_MAX === self::$_idRecorder){
+            self::$_idRecorder = 0;
+        }
+        // Check application layer protocol class.
+        if (!isset(self::$_builtinTransports[$scheme])) {
+            $scheme         = \ucfirst($scheme);
+            $this->protocol = '\\Protocols\\' . $scheme;
+            if (!\class_exists($this->protocol)) {
+                $this->protocol = "\\Workerman\\Protocols\\$scheme";
+                if (!\class_exists($this->protocol)) {
+                    throw new Exception("class \\Protocols\\$scheme not exist");
+                }
+            }
+        } else {
+            $this->transport = self::$_builtinTransports[$scheme];
+        }
+
+        // For statistics.
+        ++self::$statistics['connection_count'];
+        $this->maxSendBufferSize         = self::$defaultMaxSendBufferSize;
+        $this->maxPackageSize            = self::$defaultMaxPackageSize;
+        $this->_contextOption            = $context_option;
+        static::$connections[$this->_id] = $this;
+    }
+
+    /**
+     * Do connect.
+     *
+     * @return void
+     */
+    public function connect()
+    {
+        if ($this->_status !== self::STATUS_INITIAL && $this->_status !== self::STATUS_CLOSING &&
+            $this->_status !== self::STATUS_CLOSED) {
+            return;
+        }
+        $this->_status           = self::STATUS_CONNECTING;
+        $this->_connectStartTime = \microtime(true);
+        if ($this->transport !== 'unix') {
+            if (!$this->_remotePort) {
+                $this->_remotePort = $this->transport === 'ssl' ? 443 : 80;
+                $this->_remoteAddress = $this->_remoteHost.':'.$this->_remotePort;
+            }
+            // Open socket connection asynchronously.
+            if ($this->_contextOption) {
+                $context = \stream_context_create($this->_contextOption);
+                $this->_socket = \stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
+                    $errno, $errstr, 0, \STREAM_CLIENT_ASYNC_CONNECT, $context);
+            } else {
+                $this->_socket = \stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
+                    $errno, $errstr, 0, \STREAM_CLIENT_ASYNC_CONNECT);
+            }
+        } else {
+            $this->_socket = \stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0,
+                \STREAM_CLIENT_ASYNC_CONNECT);
+        }
+        // If failed attempt to emit onError callback.
+        if (!$this->_socket || !\is_resource($this->_socket)) {
+            $this->emitError(\WORKERMAN_CONNECT_FAIL, $errstr);
+            if ($this->_status === self::STATUS_CLOSING) {
+                $this->destroy();
+            }
+            if ($this->_status === self::STATUS_CLOSED) {
+                $this->onConnect = null;
+            }
+            return;
+        }
+        // Add socket to global event loop waiting connection is successfully established or faild.
+        Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection'));
+        // For windows.
+        if(\DIRECTORY_SEPARATOR === '\\') {
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_EXCEPT, array($this, 'checkConnection'));
+        }
+    }
+
+    /**
+     * Reconnect.
+     *
+     * @param int $after
+     * @return void
+     */
+    public function reconnect($after = 0)
+    {
+        $this->_status                   = self::STATUS_INITIAL;
+        static::$connections[$this->_id] = $this;
+        if ($this->_reconnectTimer) {
+            Timer::del($this->_reconnectTimer);
+        }
+        if ($after > 0) {
+            $this->_reconnectTimer = Timer::add($after, array($this, 'connect'), null, false);
+            return;
+        }
+        $this->connect();
+    }
+
+    /**
+     * CancelReconnect.
+     */
+    public function cancelReconnect()
+    {
+        if ($this->_reconnectTimer) {
+            Timer::del($this->_reconnectTimer);
+        }
+    }
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    public function getRemoteHost()
+    {
+        return $this->_remoteHost;
+    }
+
+    /**
+     * Get remote URI.
+     *
+     * @return string
+     */
+    public function getRemoteURI()
+    {
+        return $this->_remoteURI;
+    }
+
+    /**
+     * Try to emit onError callback.
+     *
+     * @param int    $code
+     * @param string $msg
+     * @return void
+     */
+    protected function emitError($code, $msg)
+    {
+        $this->_status = self::STATUS_CLOSING;
+        if ($this->onError) {
+            try {
+                \call_user_func($this->onError, $this, $code, $msg);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        }
+    }
+
+    /**
+     * Check connection is successfully established or faild.
+     *
+     * @param resource $socket
+     * @return void
+     */
+    public function checkConnection()
+    {
+        // Remove EV_EXPECT for windows.
+        if(\DIRECTORY_SEPARATOR === '\\') {
+            Worker::$globalEvent->del($this->_socket, EventInterface::EV_EXCEPT);
+        }
+
+        // Remove write listener.
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
+
+        if ($this->_status !== self::STATUS_CONNECTING) {
+            return;
+        }
+
+        // Check socket state.
+        if ($address = \stream_socket_get_name($this->_socket, true)) {
+            // Nonblocking.
+            \stream_set_blocking($this->_socket, false);
+            // Compatible with hhvm
+            if (\function_exists('stream_set_read_buffer')) {
+                \stream_set_read_buffer($this->_socket, 0);
+            }
+            // Try to open keepalive for tcp and disable Nagle algorithm.
+            if (\function_exists('socket_import_stream') && $this->transport === 'tcp') {
+                $raw_socket = \socket_import_stream($this->_socket);
+                \socket_set_option($raw_socket, \SOL_SOCKET, \SO_KEEPALIVE, 1);
+                \socket_set_option($raw_socket, \SOL_TCP, \TCP_NODELAY, 1);
+            }
+
+            // SSL handshake.
+            if ($this->transport === 'ssl') {
+                $this->_sslHandshakeCompleted = $this->doSslHandshake($this->_socket);
+                if ($this->_sslHandshakeCompleted === false) {
+                    return;
+                }
+            } else {
+                // There are some data waiting to send.
+                if ($this->_sendBuffer) {
+                    Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+                }
+            }
+
+            // Register a listener waiting read event.
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+
+            $this->_status                = self::STATUS_ESTABLISHED;
+            $this->_remoteAddress         = $address;
+
+            // Try to emit onConnect callback.
+            if ($this->onConnect) {
+                try {
+                    \call_user_func($this->onConnect, $this);
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+            }
+            // Try to emit protocol::onConnect
+            if ($this->protocol && \method_exists($this->protocol, 'onConnect')) {
+                try {
+                    \call_user_func(array($this->protocol, 'onConnect'), $this);
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+            }
+        } else {
+            // Connection failed.
+            $this->emitError(\WORKERMAN_CONNECT_FAIL, 'connect ' . $this->_remoteAddress . ' fail after ' . round(\microtime(true) - $this->_connectStartTime, 4) . ' seconds');
+            if ($this->_status === self::STATUS_CLOSING) {
+                $this->destroy();
+            }
+            if ($this->_status === self::STATUS_CLOSED) {
+                $this->onConnect = null;
+            }
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Connection/AsyncUdpConnection.php b/vendor/workerman/workerman/Connection/AsyncUdpConnection.php
new file mode 100644
index 0000000..745f060
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/AsyncUdpConnection.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+use Workerman\Events\EventInterface;
+use Workerman\Worker;
+use \Exception;
+
+/**
+ * AsyncUdpConnection.
+ */
+class AsyncUdpConnection extends UdpConnection
+{
+    /**
+     * Emitted when socket connection is successfully established.
+     *
+     * @var callable
+     */
+    public $onConnect = null;
+
+    /**
+     * Emitted when socket connection closed.
+     *
+     * @var callable
+     */
+    public $onClose = null;
+
+    /**
+     * Connected or not.
+     *
+     * @var bool
+     */
+    protected $connected = false;
+
+    /**
+     * Context option.
+     *
+     * @var array
+     */
+    protected $_contextOption = null;
+
+    /**
+     * Construct.
+     *
+     * @param string $remote_address
+     * @throws Exception
+     */
+    public function __construct($remote_address, $context_option = null)
+    {
+        // Get the application layer communication protocol and listening address.
+        list($scheme, $address) = \explode(':', $remote_address, 2);
+        // Check application layer protocol class.
+        if ($scheme !== 'udp') {
+            $scheme         = \ucfirst($scheme);
+            $this->protocol = '\\Protocols\\' . $scheme;
+            if (!\class_exists($this->protocol)) {
+                $this->protocol = "\\Workerman\\Protocols\\$scheme";
+                if (!\class_exists($this->protocol)) {
+                    throw new Exception("class \\Protocols\\$scheme not exist");
+                }
+            }
+        }
+        
+        $this->_remoteAddress = \substr($address, 2);
+        $this->_contextOption = $context_option;
+    }
+    
+    /**
+     * For udp package.
+     *
+     * @param resource $socket
+     * @return bool
+     */
+    public function baseRead($socket)
+    {
+        $recv_buffer = \stream_socket_recvfrom($socket, Worker::MAX_UDP_PACKAGE_SIZE, 0, $remote_address);
+        if (false === $recv_buffer || empty($remote_address)) {
+            return false;
+        }
+        
+        if ($this->onMessage) {
+            if ($this->protocol) {
+                $parser      = $this->protocol;
+                $recv_buffer = $parser::decode($recv_buffer, $this);
+            }
+            ++ConnectionInterface::$statistics['total_request'];
+            try {
+                \call_user_func($this->onMessage, $this, $recv_buffer);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param string $send_buffer
+     * @param bool   $raw
+     * @return void|boolean
+     */
+    public function send($send_buffer, $raw = false)
+    {
+        if (false === $raw && $this->protocol) {
+            $parser      = $this->protocol;
+            $send_buffer = $parser::encode($send_buffer, $this);
+            if ($send_buffer === '') {
+                return;
+            }
+        }
+        if ($this->connected === false) {
+            $this->connect();
+        }
+        return \strlen($send_buffer) === \stream_socket_sendto($this->_socket, $send_buffer, 0);
+    }
+    
+    
+    /**
+     * Close connection.
+     *
+     * @param mixed $data
+     * @param bool $raw
+     *
+     * @return bool
+     */
+    public function close($data = null, $raw = false)
+    {
+        if ($data !== null) {
+            $this->send($data, $raw);
+        }
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
+        \fclose($this->_socket);
+        $this->connected = false;
+        // Try to emit onClose callback.
+        if ($this->onClose) {
+            try {
+                \call_user_func($this->onClose, $this);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        }
+        $this->onConnect = $this->onMessage = $this->onClose = null;
+        return true;
+    }
+
+    /**
+     * Connect.
+     *
+     * @return void
+     */
+    public function connect()
+    {
+        if ($this->connected === true) {
+            return;
+        }
+        if ($this->_contextOption) {
+            $context = \stream_context_create($this->_contextOption);
+            $this->_socket = \stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg,
+                30, \STREAM_CLIENT_CONNECT, $context);
+        } else {
+            $this->_socket = \stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg);
+        }
+
+        if (!$this->_socket) {
+            Worker::safeEcho(new \Exception($errmsg));
+            return;
+        }
+        
+        \stream_set_blocking($this->_socket, false);
+        
+        if ($this->onMessage) {
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+        }
+        $this->connected = true;
+        // Try to emit onConnect callback.
+        if ($this->onConnect) {
+            try {
+                \call_user_func($this->onConnect, $this);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        }
+    }
+
+}
diff --git a/vendor/workerman/workerman/Connection/ConnectionInterface.php b/vendor/workerman/workerman/Connection/ConnectionInterface.php
new file mode 100644
index 0000000..4d3f5e1
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/ConnectionInterface.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+/**
+ * ConnectionInterface.
+ */
+abstract class  ConnectionInterface
+{
+    /**
+     * Statistics for status command.
+     *
+     * @var array
+     */
+    public static $statistics = array(
+        'connection_count' => 0,
+        'total_request'    => 0,
+        'throw_exception'  => 0,
+        'send_fail'        => 0,
+    );
+
+    /**
+     * Emitted when data is received.
+     *
+     * @var callable
+     */
+    public $onMessage = null;
+
+    /**
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
+     * @var callable
+     */
+    public $onClose = null;
+
+    /**
+     * Emitted when an error occurs with connection.
+     *
+     * @var callable
+     */
+    public $onError = null;
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param mixed $send_buffer
+     * @return void|boolean
+     */
+    abstract public function send($send_buffer);
+
+    /**
+     * Get remote IP.
+     *
+     * @return string
+     */
+    abstract public function getRemoteIp();
+
+    /**
+     * Get remote port.
+     *
+     * @return int
+     */
+    abstract public function getRemotePort();
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    abstract public function getRemoteAddress();
+
+    /**
+     * Get local IP.
+     *
+     * @return string
+     */
+    abstract public function getLocalIp();
+
+    /**
+     * Get local port.
+     *
+     * @return int
+     */
+    abstract public function getLocalPort();
+
+    /**
+     * Get local address.
+     *
+     * @return string
+     */
+    abstract public function getLocalAddress();
+
+    /**
+     * Is ipv4.
+     *
+     * @return bool
+     */
+    abstract public function isIPv4();
+
+    /**
+     * Is ipv6.
+     *
+     * @return bool
+     */
+    abstract public function isIPv6();
+
+    /**
+     * Close connection.
+     *
+     * @param string|null $data
+     * @return void
+     */
+    abstract public function close($data = null);
+}
diff --git a/vendor/workerman/workerman/Connection/TcpConnection.php b/vendor/workerman/workerman/Connection/TcpConnection.php
new file mode 100644
index 0000000..4984a50
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/TcpConnection.php
@@ -0,0 +1,974 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+use Workerman\Events\EventInterface;
+use Workerman\Worker;
+use \Exception;
+
+/**
+ * TcpConnection.
+ */
+class TcpConnection extends ConnectionInterface
+{
+    /**
+     * Read buffer size.
+     *
+     * @var int
+     */
+    const READ_BUFFER_SIZE = 65535;
+
+    /**
+     * Status initial.
+     *
+     * @var int
+     */
+    const STATUS_INITIAL = 0;
+
+    /**
+     * Status connecting.
+     *
+     * @var int
+     */
+    const STATUS_CONNECTING = 1;
+
+    /**
+     * Status connection established.
+     *
+     * @var int
+     */
+    const STATUS_ESTABLISHED = 2;
+
+    /**
+     * Status closing.
+     *
+     * @var int
+     */
+    const STATUS_CLOSING = 4;
+
+    /**
+     * Status closed.
+     *
+     * @var int
+     */
+    const STATUS_CLOSED = 8;
+
+    /**
+     * Emitted when data is received.
+     *
+     * @var callable
+     */
+    public $onMessage = null;
+
+    /**
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
+     * @var callable
+     */
+    public $onClose = null;
+
+    /**
+     * Emitted when an error occurs with connection.
+     *
+     * @var callable
+     */
+    public $onError = null;
+
+    /**
+     * Emitted when the send buffer becomes full.
+     *
+     * @var callable
+     */
+    public $onBufferFull = null;
+
+    /**
+     * Emitted when the send buffer becomes empty.
+     *
+     * @var callable
+     */
+    public $onBufferDrain = null;
+
+    /**
+     * Application layer protocol.
+     * The format is like this Workerman\\Protocols\\Http.
+     *
+     * @var \Workerman\Protocols\ProtocolInterface
+     */
+    public $protocol = null;
+
+    /**
+     * Transport (tcp/udp/unix/ssl).
+     *
+     * @var string
+     */
+    public $transport = 'tcp';
+
+    /**
+     * Which worker belong to.
+     *
+     * @var Worker
+     */
+    public $worker = null;
+
+    /**
+     * Bytes read.
+     *
+     * @var int
+     */
+    public $bytesRead = 0;
+
+    /**
+     * Bytes written.
+     *
+     * @var int
+     */
+    public $bytesWritten = 0;
+
+    /**
+     * Connection->id.
+     *
+     * @var int
+     */
+    public $id = 0;
+
+    /**
+     * A copy of $worker->id which used to clean up the connection in worker->connections
+     *
+     * @var int
+     */
+    protected $_id = 0;
+
+    /**
+     * Sets the maximum send buffer size for the current connection.
+     * OnBufferFull callback will be emited When the send buffer is full.
+     *
+     * @var int
+     */
+    public $maxSendBufferSize = 1048576;
+
+    /**
+     * Default send buffer size.
+     *
+     * @var int
+     */
+    public static $defaultMaxSendBufferSize = 1048576;
+
+    /**
+     * Sets the maximum acceptable packet size for the current connection.
+     *
+     * @var int
+     */
+    public $maxPackageSize = 1048576;
+    
+    /**
+     * Default maximum acceptable packet size.
+     *
+     * @var int
+     */
+    public static $defaultMaxPackageSize = 10485760;
+
+    /**
+     * Id recorder.
+     *
+     * @var int
+     */
+    protected static $_idRecorder = 1;
+
+    /**
+     * Socket
+     *
+     * @var resource
+     */
+    protected $_socket = null;
+
+    /**
+     * Send buffer.
+     *
+     * @var string
+     */
+    protected $_sendBuffer = '';
+
+    /**
+     * Receive buffer.
+     *
+     * @var string
+     */
+    protected $_recvBuffer = '';
+
+    /**
+     * Current package length.
+     *
+     * @var int
+     */
+    protected $_currentPackageLength = 0;
+
+    /**
+     * Connection status.
+     *
+     * @var int
+     */
+    protected $_status = self::STATUS_ESTABLISHED;
+
+    /**
+     * Remote address.
+     *
+     * @var string
+     */
+    protected $_remoteAddress = '';
+
+    /**
+     * Is paused.
+     *
+     * @var bool
+     */
+    protected $_isPaused = false;
+
+    /**
+     * SSL handshake completed or not.
+     *
+     * @var bool
+     */
+    protected $_sslHandshakeCompleted = false;
+
+    /**
+     * All connection instances.
+     *
+     * @var array
+     */
+    public static $connections = array();
+
+    /**
+     * Status to string.
+     *
+     * @var array
+     */
+    public static $_statusToString = array(
+        self::STATUS_INITIAL     => 'INITIAL',
+        self::STATUS_CONNECTING  => 'CONNECTING',
+        self::STATUS_ESTABLISHED => 'ESTABLISHED',
+        self::STATUS_CLOSING     => 'CLOSING',
+        self::STATUS_CLOSED      => 'CLOSED',
+    );
+
+    /**
+     * Construct.
+     *
+     * @param resource $socket
+     * @param string   $remote_address
+     */
+    public function __construct($socket, $remote_address = '')
+    {
+        ++self::$statistics['connection_count'];
+        $this->id = $this->_id = self::$_idRecorder++;
+        if(self::$_idRecorder === \PHP_INT_MAX){
+            self::$_idRecorder = 0;
+        }
+        $this->_socket = $socket;
+        \stream_set_blocking($this->_socket, 0);
+        // Compatible with hhvm
+        if (\function_exists('stream_set_read_buffer')) {
+            \stream_set_read_buffer($this->_socket, 0);
+        }
+        Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+        $this->maxSendBufferSize        = self::$defaultMaxSendBufferSize;
+        $this->maxPackageSize           = self::$defaultMaxPackageSize;
+        $this->_remoteAddress           = $remote_address;
+        static::$connections[$this->id] = $this;
+    }
+
+    /**
+     * Get status.
+     *
+     * @param bool $raw_output
+     *
+     * @return int|string
+     */
+    public function getStatus($raw_output = true)
+    {
+        if ($raw_output) {
+            return $this->_status;
+        }
+        return self::$_statusToString[$this->_status];
+    }
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param mixed $send_buffer
+     * @param bool  $raw
+     * @return bool|null
+     */
+    public function send($send_buffer, $raw = false)
+    {
+        if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
+            return false;
+        }
+
+        // Try to call protocol::encode($send_buffer) before sending.
+        if (false === $raw && $this->protocol !== null) {
+            $parser      = $this->protocol;
+            $send_buffer = $parser::encode($send_buffer, $this);
+            if ($send_buffer === '') {
+                return;
+            }
+        }
+
+        if ($this->_status !== self::STATUS_ESTABLISHED ||
+            ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true)
+        ) {
+            if ($this->_sendBuffer && $this->bufferIsFull()) {
+                ++self::$statistics['send_fail'];
+                return false;
+            }
+            $this->_sendBuffer .= $send_buffer;
+            $this->checkBufferWillFull();
+            return;
+        }
+
+        // Attempt to send data directly.
+        if ($this->_sendBuffer === '') {
+            if ($this->transport === 'ssl') {
+                Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+                $this->_sendBuffer = $send_buffer;
+                $this->checkBufferWillFull();
+                return;
+            }
+            $len = 0;
+            try {
+                $len = @\fwrite($this->_socket, $send_buffer);
+            } catch (\Exception $e) {
+                Worker::log($e);
+            } catch (\Error $e) {
+                Worker::log($e);
+            }
+            // send successful.
+            if ($len === \strlen($send_buffer)) {
+                $this->bytesWritten += $len;
+                return true;
+            }
+            // Send only part of the data.
+            if ($len > 0) {
+                $this->_sendBuffer = \substr($send_buffer, $len);
+                $this->bytesWritten += $len;
+            } else {
+                // Connection closed?
+                if (!\is_resource($this->_socket) || \feof($this->_socket)) {
+                    ++self::$statistics['send_fail'];
+                    if ($this->onError) {
+                        try {
+                            \call_user_func($this->onError, $this, \WORKERMAN_SEND_FAIL, 'client closed');
+                        } catch (\Exception $e) {
+                            Worker::stopAll(250, $e);
+                        } catch (\Error $e) {
+                            Worker::stopAll(250, $e);
+                        }
+                    }
+                    $this->destroy();
+                    return false;
+                }
+                $this->_sendBuffer = $send_buffer;
+            }
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+            // Check if the send buffer will be full.
+            $this->checkBufferWillFull();
+            return;
+        }
+
+        if ($this->bufferIsFull()) {
+            ++self::$statistics['send_fail'];
+            return false;
+        }
+
+        $this->_sendBuffer .= $send_buffer;
+        // Check if the send buffer is full.
+        $this->checkBufferWillFull();
+    }
+
+    /**
+     * Get remote IP.
+     *
+     * @return string
+     */
+    public function getRemoteIp()
+    {
+        $pos = \strrpos($this->_remoteAddress, ':');
+        if ($pos) {
+            return (string) \substr($this->_remoteAddress, 0, $pos);
+        }
+        return '';
+    }
+
+    /**
+     * Get remote port.
+     *
+     * @return int
+     */
+    public function getRemotePort()
+    {
+        if ($this->_remoteAddress) {
+            return (int) \substr(\strrchr($this->_remoteAddress, ':'), 1);
+        }
+        return 0;
+    }
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    public function getRemoteAddress()
+    {
+        return $this->_remoteAddress;
+    }
+
+    /**
+     * Get local IP.
+     *
+     * @return string
+     */
+    public function getLocalIp()
+    {
+        $address = $this->getLocalAddress();
+        $pos = \strrpos($address, ':');
+        if (!$pos) {
+            return '';
+        }
+        return \substr($address, 0, $pos);
+    }
+
+    /**
+     * Get local port.
+     *
+     * @return int
+     */
+    public function getLocalPort()
+    {
+        $address = $this->getLocalAddress();
+        $pos = \strrpos($address, ':');
+        if (!$pos) {
+            return 0;
+        }
+        return (int)\substr(\strrchr($address, ':'), 1);
+    }
+
+    /**
+     * Get local address.
+     *
+     * @return string
+     */
+    public function getLocalAddress()
+    {
+        if (!\is_resource($this->_socket)) {
+            return '';
+        }
+        return (string)@\stream_socket_get_name($this->_socket, false);
+    }
+
+    /**
+     * Get send buffer queue size.
+     *
+     * @return integer
+     */
+    public function getSendBufferQueueSize()
+    {
+        return \strlen($this->_sendBuffer);
+    }
+
+    /**
+     * Get recv buffer queue size.
+     *
+     * @return integer
+     */
+    public function getRecvBufferQueueSize()
+    {
+        return \strlen($this->_recvBuffer);
+    }
+
+    /**
+     * Is ipv4.
+     *
+     * return bool.
+     */
+    public function isIpV4()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return \strpos($this->getRemoteIp(), ':') === false;
+    }
+
+    /**
+     * Is ipv6.
+     *
+     * return bool.
+     */
+    public function isIpV6()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return \strpos($this->getRemoteIp(), ':') !== false;
+    }
+
+    /**
+     * Pauses the reading of data. That is onMessage will not be emitted. Useful to throttle back an upload.
+     *
+     * @return void
+     */
+    public function pauseRecv()
+    {
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
+        $this->_isPaused = true;
+    }
+
+    /**
+     * Resumes reading after a call to pauseRecv.
+     *
+     * @return void
+     */
+    public function resumeRecv()
+    {
+        if ($this->_isPaused === true) {
+            Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
+            $this->_isPaused = false;
+            $this->baseRead($this->_socket, false);
+        }
+    }
+
+
+
+    /**
+     * Base read handler.
+     *
+     * @param resource $socket
+     * @param bool $check_eof
+     * @return void
+     */
+    public function baseRead($socket, $check_eof = true)
+    {
+        // SSL handshake.
+        if ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) {
+            if ($this->doSslHandshake($socket)) {
+                $this->_sslHandshakeCompleted = true;
+                if ($this->_sendBuffer) {
+                    Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
+                }
+            } else {
+                return;
+            }
+        }
+
+        $buffer = '';
+        try {
+            $buffer = @\fread($socket, self::READ_BUFFER_SIZE);
+        } catch (\Exception $e) {} catch (\Error $e) {}
+
+        // Check connection closed.
+        if ($buffer === '' || $buffer === false) {
+            if ($check_eof && (\feof($socket) || !\is_resource($socket) || $buffer === false)) {
+                $this->destroy();
+                return;
+            }
+        } else {
+            $this->bytesRead += \strlen($buffer);
+            $this->_recvBuffer .= $buffer;
+        }
+
+        // If the application layer protocol has been set up.
+        if ($this->protocol !== null) {
+            $parser = $this->protocol;
+            while ($this->_recvBuffer !== '' && !$this->_isPaused) {
+                // The current packet length is known.
+                if ($this->_currentPackageLength) {
+                    // Data is not enough for a package.
+                    if ($this->_currentPackageLength > \strlen($this->_recvBuffer)) {
+                        break;
+                    }
+                } else {
+                    // Get current package length.
+                    try {
+                        $this->_currentPackageLength = $parser::input($this->_recvBuffer, $this);
+                    } catch (\Exception $e) {} catch (\Error $e) {}
+                    // The packet length is unknown.
+                    if ($this->_currentPackageLength === 0) {
+                        break;
+                    } elseif ($this->_currentPackageLength > 0 && $this->_currentPackageLength <= $this->maxPackageSize) {
+                        // Data is not enough for a package.
+                        if ($this->_currentPackageLength > \strlen($this->_recvBuffer)) {
+                            break;
+                        }
+                    } // Wrong package.
+                    else {
+                        Worker::safeEcho('Error package. package_length=' . \var_export($this->_currentPackageLength, true));
+                        $this->destroy();
+                        return;
+                    }
+                }
+
+                // The data is enough for a packet.
+                ++self::$statistics['total_request'];
+                // The current packet length is equal to the length of the buffer.
+                if (\strlen($this->_recvBuffer) === $this->_currentPackageLength) {
+                    $one_request_buffer = $this->_recvBuffer;
+                    $this->_recvBuffer  = '';
+                } else {
+                    // Get a full package from the buffer.
+                    $one_request_buffer = \substr($this->_recvBuffer, 0, $this->_currentPackageLength);
+                    // Remove the current package from the receive buffer.
+                    $this->_recvBuffer = \substr($this->_recvBuffer, $this->_currentPackageLength);
+                }
+                // Reset the current packet length to 0.
+                $this->_currentPackageLength = 0;
+                if (!$this->onMessage) {
+                    continue;
+                }
+                try {
+                    // Decode request buffer before Emitting onMessage callback.
+                    \call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+            }
+            return;
+        }
+
+        if ($this->_recvBuffer === '' || $this->_isPaused) {
+            return;
+        }
+
+        // Applications protocol is not set.
+        ++self::$statistics['total_request'];
+        if (!$this->onMessage) {
+            $this->_recvBuffer = '';
+            return;
+        }
+        try {
+            \call_user_func($this->onMessage, $this, $this->_recvBuffer);
+        } catch (\Exception $e) {
+            Worker::stopAll(250, $e);
+        } catch (\Error $e) {
+            Worker::stopAll(250, $e);
+        }
+        // Clean receive buffer.
+        $this->_recvBuffer = '';
+    }
+
+    /**
+     * Base write handler.
+     *
+     * @return void|bool
+     */
+    public function baseWrite()
+    {
+        \set_error_handler(function(){});
+        if ($this->transport === 'ssl') {
+            $len = @\fwrite($this->_socket, $this->_sendBuffer, 8192);
+        } else {
+            $len = @\fwrite($this->_socket, $this->_sendBuffer);
+        }
+        \restore_error_handler();
+        if ($len === \strlen($this->_sendBuffer)) {
+            $this->bytesWritten += $len;
+            Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
+            $this->_sendBuffer = '';
+            // Try to emit onBufferDrain callback when the send buffer becomes empty.
+            if ($this->onBufferDrain) {
+                try {
+                    \call_user_func($this->onBufferDrain, $this);
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+            }
+            if ($this->_status === self::STATUS_CLOSING) {
+                $this->destroy();
+            }
+            return true;
+        }
+        if ($len > 0) {
+            $this->bytesWritten += $len;
+            $this->_sendBuffer = \substr($this->_sendBuffer, $len);
+        } else {
+            ++self::$statistics['send_fail'];
+            $this->destroy();
+        }
+    }
+
+    /**
+     * SSL handshake.
+     *
+     * @param resource $socket
+     * @return bool
+     */
+    public function doSslHandshake($socket){
+        if (\feof($socket)) {
+            $this->destroy();
+            return false;
+        }
+        $async = $this instanceof AsyncTcpConnection;
+        
+        /**
+          *  We disabled ssl3 because https://blog.qualys.com/ssllabs/2014/10/15/ssl-3-is-dead-killed-by-the-poodle-attack.
+          *  You can enable ssl3 by the codes below.
+          */
+        /*if($async){
+            $type = STREAM_CRYPTO_METHOD_SSLv2_CLIENT | STREAM_CRYPTO_METHOD_SSLv23_CLIENT | STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
+        }else{
+            $type = STREAM_CRYPTO_METHOD_SSLv2_SERVER | STREAM_CRYPTO_METHOD_SSLv23_SERVER | STREAM_CRYPTO_METHOD_SSLv3_SERVER;
+        }*/
+        
+        if($async){
+            $type = \STREAM_CRYPTO_METHOD_SSLv2_CLIENT | \STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
+        }else{
+            $type = \STREAM_CRYPTO_METHOD_SSLv2_SERVER | \STREAM_CRYPTO_METHOD_SSLv23_SERVER;
+        }
+        
+        // Hidden error.
+        \set_error_handler(function($errno, $errstr, $file){
+            if (!Worker::$daemonize) {
+                Worker::safeEcho("SSL handshake error: $errstr \n");
+            }
+        });
+        $ret = \stream_socket_enable_crypto($socket, true, $type);
+        \restore_error_handler();
+        // Negotiation has failed.
+        if (false === $ret) {
+            $this->destroy();
+            return false;
+        } elseif (0 === $ret) {
+            // There isn't enough data and should try again.
+            return 0;
+        }
+        if (isset($this->onSslHandshake)) {
+            try {
+                \call_user_func($this->onSslHandshake, $this);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This method pulls all the data out of a readable stream, and writes it to the supplied destination.
+     *
+     * @param self $dest
+     * @return void
+     */
+    public function pipe(self $dest)
+    {
+        $source              = $this;
+        $this->onMessage     = function ($source, $data) use ($dest) {
+            $dest->send($data);
+        };
+        $this->onClose       = function ($source) use ($dest) {
+            $dest->close();
+        };
+        $dest->onBufferFull  = function ($dest) use ($source) {
+            $source->pauseRecv();
+        };
+        $dest->onBufferDrain = function ($dest) use ($source) {
+            $source->resumeRecv();
+        };
+    }
+
+    /**
+     * Remove $length of data from receive buffer.
+     *
+     * @param int $length
+     * @return void
+     */
+    public function consumeRecvBuffer($length)
+    {
+        $this->_recvBuffer = \substr($this->_recvBuffer, $length);
+    }
+
+    /**
+     * Close connection.
+     *
+     * @param mixed $data
+     * @param bool $raw
+     * @return void
+     */
+    public function close($data = null, $raw = false)
+    {
+        if($this->_status === self::STATUS_CONNECTING){
+            $this->destroy();
+            return;
+        }
+
+        if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
+            return;
+        }
+
+        if ($data !== null) {
+            $this->send($data, $raw);
+        }
+
+        $this->_status = self::STATUS_CLOSING;
+        
+        if ($this->_sendBuffer === '') {
+            $this->destroy();
+        } else {
+            $this->pauseRecv();
+        }
+    }
+
+    /**
+     * Get the real socket.
+     *
+     * @return resource
+     */
+    public function getSocket()
+    {
+        return $this->_socket;
+    }
+
+    /**
+     * Check whether the send buffer will be full.
+     *
+     * @return void
+     */
+    protected function checkBufferWillFull()
+    {
+        if ($this->maxSendBufferSize <= \strlen($this->_sendBuffer)) {
+            if ($this->onBufferFull) {
+                try {
+                    \call_user_func($this->onBufferFull, $this);
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Whether send buffer is full.
+     *
+     * @return bool
+     */
+    protected function bufferIsFull()
+    {
+        // Buffer has been marked as full but still has data to send then the packet is discarded.
+        if ($this->maxSendBufferSize <= \strlen($this->_sendBuffer)) {
+            if ($this->onError) {
+                try {
+                    \call_user_func($this->onError, $this, \WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * Whether send buffer is Empty.
+     *
+     * @return bool
+     */
+    public function bufferIsEmpty()
+    {
+    	return empty($this->_sendBuffer);
+    }
+
+    /**
+     * Destroy connection.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        // Avoid repeated calls.
+        if ($this->_status === self::STATUS_CLOSED) {
+            return;
+        }
+        // Remove event listener.
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
+        Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
+
+        // Close socket.
+        try {
+            @\fclose($this->_socket);
+        } catch (\Exception $e) {} catch (\Error $e) {}
+
+        $this->_status = self::STATUS_CLOSED;
+        // Try to emit onClose callback.
+        if ($this->onClose) {
+            try {
+                \call_user_func($this->onClose, $this);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        }
+        // Try to emit protocol::onClose
+        if ($this->protocol && \method_exists($this->protocol, 'onClose')) {
+            try {
+                \call_user_func(array($this->protocol, 'onClose'), $this);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        }
+        $this->_sendBuffer = $this->_recvBuffer = '';
+        $this->_currentPackageLength = 0;
+        $this->_isPaused = $this->_sslHandshakeCompleted = false;
+        if ($this->_status === self::STATUS_CLOSED) {
+            // Cleaning up the callback to avoid memory leaks.
+            $this->onMessage = $this->onClose = $this->onError = $this->onBufferFull = $this->onBufferDrain = null;
+            // Remove from worker->connections.
+            if ($this->worker) {
+                unset($this->worker->connections[$this->_id]);
+            }
+            unset(static::$connections[$this->_id]);
+        }
+    }
+
+    /**
+     * Destruct.
+     *
+     * @return void
+     */
+    public function __destruct()
+    {
+        static $mod;
+        self::$statistics['connection_count']--;
+        if (Worker::getGracefulStop()) {
+            if (!isset($mod)) {
+                $mod = \ceil((self::$statistics['connection_count'] + 1) / 3);
+            }
+
+            if (0 === self::$statistics['connection_count'] % $mod) {
+                Worker::log('worker[' . \posix_getpid() . '] remains ' . self::$statistics['connection_count'] . ' connection(s)');
+            }
+
+            if(0 === self::$statistics['connection_count']) {
+                Worker::stopAll();
+            }
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Connection/UdpConnection.php b/vendor/workerman/workerman/Connection/UdpConnection.php
new file mode 100644
index 0000000..2974129
--- /dev/null
+++ b/vendor/workerman/workerman/Connection/UdpConnection.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Connection;
+
+/**
+ * UdpConnection.
+ */
+class UdpConnection extends ConnectionInterface
+{
+    /**
+     * Application layer protocol.
+     * The format is like this Workerman\\Protocols\\Http.
+     *
+     * @var \Workerman\Protocols\ProtocolInterface
+     */
+    public $protocol = null;
+
+    /**
+     * Transport layer protocol.
+     *
+     * @var string
+     */
+    public $transport = 'udp';
+
+    /**
+     * Udp socket.
+     *
+     * @var resource
+     */
+    protected $_socket = null;
+
+    /**
+     * Remote address.
+     *
+     * @var string
+     */
+    protected $_remoteAddress = '';
+
+    /**
+     * Construct.
+     *
+     * @param resource $socket
+     * @param string   $remote_address
+     */
+    public function __construct($socket, $remote_address)
+    {
+        $this->_socket        = $socket;
+        $this->_remoteAddress = $remote_address;
+    }
+
+    /**
+     * Sends data on the connection.
+     *
+     * @param string $send_buffer
+     * @param bool   $raw
+     * @return void|boolean
+     */
+    public function send($send_buffer, $raw = false)
+    {
+        if (false === $raw && $this->protocol) {
+            $parser      = $this->protocol;
+            $send_buffer = $parser::encode($send_buffer, $this);
+            if ($send_buffer === '') {
+                return;
+            }
+        }
+        return \strlen($send_buffer) === \stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress);
+    }
+
+    /**
+     * Get remote IP.
+     *
+     * @return string
+     */
+    public function getRemoteIp()
+    {
+        $pos = \strrpos($this->_remoteAddress, ':');
+        if ($pos) {
+            return \trim(\substr($this->_remoteAddress, 0, $pos), '[]');
+        }
+        return '';
+    }
+
+    /**
+     * Get remote port.
+     *
+     * @return int
+     */
+    public function getRemotePort()
+    {
+        if ($this->_remoteAddress) {
+            return (int)\substr(\strrchr($this->_remoteAddress, ':'), 1);
+        }
+        return 0;
+    }
+
+    /**
+     * Get remote address.
+     *
+     * @return string
+     */
+    public function getRemoteAddress()
+    {
+        return $this->_remoteAddress;
+    }
+
+    /**
+     * Get local IP.
+     *
+     * @return string
+     */
+    public function getLocalIp()
+    {
+        $address = $this->getLocalAddress();
+        $pos = \strrpos($address, ':');
+        if (!$pos) {
+            return '';
+        }
+        return \substr($address, 0, $pos);
+    }
+
+    /**
+     * Get local port.
+     *
+     * @return int
+     */
+    public function getLocalPort()
+    {
+        $address = $this->getLocalAddress();
+        $pos = \strrpos($address, ':');
+        if (!$pos) {
+            return 0;
+        }
+        return (int)\substr(\strrchr($address, ':'), 1);
+    }
+
+    /**
+     * Get local address.
+     *
+     * @return string
+     */
+    public function getLocalAddress()
+    {
+        return (string)@\stream_socket_get_name($this->_socket, false);
+    }
+
+    /**
+     * Is ipv4.
+     *
+     * @return bool.
+     */
+    public function isIpV4()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return \strpos($this->getRemoteIp(), ':') === false;
+    }
+
+    /**
+     * Is ipv6.
+     *
+     * @return bool.
+     */
+    public function isIpV6()
+    {
+        if ($this->transport === 'unix') {
+            return false;
+        }
+        return \strpos($this->getRemoteIp(), ':') !== false;
+    }
+
+    /**
+     * Close connection.
+     *
+     * @param mixed $data
+     * @param bool  $raw
+     * @return bool
+     */
+    public function close($data = null, $raw = false)
+    {
+        if ($data !== null) {
+            $this->send($data, $raw);
+        }
+        return true;
+    }
+    
+    /**
+     * Get the real socket.
+     *
+     * @return resource
+     */
+    public function getSocket()
+    {
+        return $this->_socket;
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Ev.php b/vendor/workerman/workerman/Events/Ev.php
new file mode 100644
index 0000000..82c9bdf
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Ev.php
@@ -0,0 +1,191 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author  有个鬼<42765633@qq.com>
+ * @link    http://www.workerman.net/
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Workerman\Worker;
+use \EvWatcher;
+
+/**
+ * ev eventloop
+ */
+class Ev implements EventInterface
+{
+    /**
+     * All listeners for read/write event.
+     *
+     * @var array
+     */
+    protected $_allEvents = array();
+
+    /**
+     * Event listeners of signal.
+     *
+     * @var array
+     */
+    protected $_eventSignal = array();
+
+    /**
+     * All timer event listeners.
+     * [func, args, event, flag, time_interval]
+     *
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * Timer id.
+     *
+     * @var int
+     */
+    protected static $_timerId = 1;
+
+    /**
+     * Add a timer.
+     * {@inheritdoc}
+     */
+    public function add($fd, $flag, $func, $args = null)
+    {
+        $callback = function ($event, $socket) use ($fd, $func) {
+            try {
+                \call_user_func($func, $fd);
+            } catch (\Exception $e) {
+                Worker::stopAll(250, $e);
+            } catch (\Error $e) {
+                Worker::stopAll(250, $e);
+            }
+        };
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                $event                   = new \EvSignal($fd, $callback);
+                $this->_eventSignal[$fd] = $event;
+                return true;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $repeat                             = $flag === self::EV_TIMER_ONCE ? 0 : $fd;
+                $param                              = array($func, (array)$args, $flag, $fd, self::$_timerId);
+                $event                              = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param);
+                $this->_eventTimer[self::$_timerId] = $event;
+                return self::$_timerId++;
+            default :
+                $fd_key                           = (int)$fd;
+                $real_flag                        = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE;
+                $event                            = new \EvIo($fd, $real_flag, $callback);
+                $this->_allEvents[$fd_key][$flag] = $event;
+                return true;
+        }
+
+    }
+
+    /**
+     * Remove a timer.
+     * {@inheritdoc}
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][$flag])) {
+                    $this->_allEvents[$fd_key][$flag]->stop();
+                    unset($this->_allEvents[$fd_key][$flag]);
+                }
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                break;
+            case  self::EV_SIGNAL:
+                $fd_key = (int)$fd;
+                if (isset($this->_eventSignal[$fd_key])) {
+                    $this->_eventSignal[$fd_key]->stop();
+                    unset($this->_eventSignal[$fd_key]);
+                }
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                if (isset($this->_eventTimer[$fd])) {
+                    $this->_eventTimer[$fd]->stop();
+                    unset($this->_eventTimer[$fd]);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Timer callback.
+     *
+     * @param EvWatcher $event
+     */
+    public function timerCallback(EvWatcher $event)
+    {
+        $param    = $event->data;
+        $timer_id = $param[4];
+        if ($param[2] === self::EV_TIMER_ONCE) {
+            $this->_eventTimer[$timer_id]->stop();
+            unset($this->_eventTimer[$timer_id]);
+        }
+        try {
+            \call_user_func_array($param[0], $param[1]);
+        } catch (\Exception $e) {
+            Worker::stopAll(250, $e);
+        } catch (\Error $e) {
+            Worker::stopAll(250, $e);
+        }
+    }
+
+    /**
+     * Remove all timers.
+     *
+     * @return void
+     */
+    public function clearAllTimer()
+    {
+        foreach ($this->_eventTimer as $event) {
+            $event->stop();
+        }
+        $this->_eventTimer = array();
+    }
+
+    /**
+     * Main loop.
+     *
+     * @see EventInterface::loop()
+     */
+    public function loop()
+    {
+        \Ev::run();
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        foreach ($this->_allEvents as $event) {
+            $event->stop();
+        }
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return \count($this->_eventTimer);
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Event.php b/vendor/workerman/workerman/Events/Event.php
new file mode 100644
index 0000000..9e25521
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Event.php
@@ -0,0 +1,215 @@
+<?php 
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    有个鬼<42765633@qq.com>
+ * @copyright 有个鬼<42765633@qq.com>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Workerman\Worker;
+
+/**
+ * libevent eventloop
+ */
+class Event implements EventInterface
+{
+    /**
+     * Event base.
+     * @var object
+     */
+    protected $_eventBase = null;
+    
+    /**
+     * All listeners for read/write event.
+     * @var array
+     */
+    protected $_allEvents = array();
+    
+    /**
+     * Event listeners of signal.
+     * @var array
+     */
+    protected $_eventSignal = array();
+    
+    /**
+     * All timer event listeners.
+     * [func, args, event, flag, time_interval]
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * Timer id.
+     * @var int
+     */
+    protected static $_timerId = 1;
+    
+    /**
+     * construct
+     * @return void
+     */
+    public function __construct()
+    {
+        if (\class_exists('\\\\EventBase', false)) {
+            $class_name = '\\\\EventBase';
+        } else {
+            $class_name = '\EventBase';
+        }
+        $this->_eventBase = new $class_name();
+    }
+   
+    /**
+     * @see EventInterface::add()
+     */
+    public function add($fd, $flag, $func, $args=array())
+    {
+        if (\class_exists('\\\\Event', false)) {
+            $class_name = '\\\\Event';
+        } else {
+            $class_name = '\Event';
+        }
+        switch ($flag) {
+            case self::EV_SIGNAL:
+
+                $fd_key = (int)$fd;
+                $event = $class_name::signal($this->_eventBase, $fd, $func);
+                if (!$event||!$event->add()) {
+                    return false;
+                }
+                $this->_eventSignal[$fd_key] = $event;
+                return true;
+
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+
+                $param = array($func, (array)$args, $flag, $fd, self::$_timerId);
+                $event = new $class_name($this->_eventBase, -1, $class_name::TIMEOUT|$class_name::PERSIST, array($this, "timerCallback"), $param);
+                if (!$event||!$event->addTimer($fd)) {
+                    return false;
+                }
+                $this->_eventTimer[self::$_timerId] = $event;
+                return self::$_timerId++;
+                
+            default :
+                $fd_key = (int)$fd;
+                $real_flag = $flag === self::EV_READ ? $class_name::READ | $class_name::PERSIST : $class_name::WRITE | $class_name::PERSIST;
+                $event = new $class_name($this->_eventBase, $fd, $real_flag, $func, $fd);
+                if (!$event||!$event->add()) {
+                    return false;
+                }
+                $this->_allEvents[$fd_key][$flag] = $event;
+                return true;
+        }
+    }
+    
+    /**
+     * @see Events\EventInterface::del()
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+
+            case self::EV_READ:
+            case self::EV_WRITE:
+
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][$flag])) {
+                    $this->_allEvents[$fd_key][$flag]->del();
+                    unset($this->_allEvents[$fd_key][$flag]);
+                }
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                break;
+
+            case  self::EV_SIGNAL:
+                $fd_key = (int)$fd;
+                if (isset($this->_eventSignal[$fd_key])) {
+                    $this->_eventSignal[$fd_key]->del();
+                    unset($this->_eventSignal[$fd_key]);
+                }
+                break;
+
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                if (isset($this->_eventTimer[$fd])) {
+                    $this->_eventTimer[$fd]->del();
+                    unset($this->_eventTimer[$fd]);
+                }
+                break;
+        }
+        return true;
+    }
+    
+    /**
+     * Timer callback.
+     * @param int|null $fd
+     * @param int $what
+     * @param int $timer_id
+     */
+    public function timerCallback($fd, $what, $param)
+    {
+        $timer_id = $param[4];
+        
+        if ($param[2] === self::EV_TIMER_ONCE) {
+            $this->_eventTimer[$timer_id]->del();
+            unset($this->_eventTimer[$timer_id]);
+        }
+
+        try {
+            \call_user_func_array($param[0], $param[1]);
+        } catch (\Exception $e) {
+            Worker::stopAll(250, $e);
+        } catch (\Error $e) {
+            Worker::stopAll(250, $e);
+        }
+    }
+    
+    /**
+     * @see Events\EventInterface::clearAllTimer() 
+     * @return void
+     */
+    public function clearAllTimer()
+    {
+        foreach ($this->_eventTimer as $event) {
+            $event->del();
+        }
+        $this->_eventTimer = array();
+    }
+     
+
+    /**
+     * @see EventInterface::loop()
+     */
+    public function loop()
+    {
+        $this->_eventBase->loop();
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        $this->_eventBase->exit();
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return \count($this->_eventTimer);
+    }
+}
diff --git a/vendor/workerman/workerman/Events/EventInterface.php b/vendor/workerman/workerman/Events/EventInterface.php
new file mode 100644
index 0000000..e6f59c6
--- /dev/null
+++ b/vendor/workerman/workerman/Events/EventInterface.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+interface EventInterface
+{
+    /**
+     * Read event.
+     *
+     * @var int
+     */
+    const EV_READ = 1;
+
+    /**
+     * Write event.
+     *
+     * @var int
+     */
+    const EV_WRITE = 2;
+
+    /**
+     * Except event
+     *
+     * @var int
+     */
+    const EV_EXCEPT = 3;
+
+    /**
+     * Signal event.
+     *
+     * @var int
+     */
+    const EV_SIGNAL = 4;
+
+    /**
+     * Timer event.
+     *
+     * @var int
+     */
+    const EV_TIMER = 8;
+
+    /**
+     * Timer once event.
+     *
+     * @var int
+     */
+    const EV_TIMER_ONCE = 16;
+
+    /**
+     * Add event listener to event loop.
+     *
+     * @param mixed    $fd
+     * @param int      $flag
+     * @param callable $func
+     * @param array    $args
+     * @return bool
+     */
+    public function add($fd, $flag, $func, $args = array());
+
+    /**
+     * Remove event listener from event loop.
+     *
+     * @param mixed $fd
+     * @param int   $flag
+     * @return bool
+     */
+    public function del($fd, $flag);
+
+    /**
+     * Remove all timers.
+     *
+     * @return void
+     */
+    public function clearAllTimer();
+
+    /**
+     * Main loop.
+     *
+     * @return void
+     */
+    public function loop();
+
+    /**
+     * Destroy loop.
+     *
+     * @return mixed
+     */
+    public function destroy();
+
+    /**
+     * Get Timer count.
+     *
+     * @return mixed
+     */
+    public function getTimerCount();
+}
diff --git a/vendor/workerman/workerman/Events/Libevent.php b/vendor/workerman/workerman/Events/Libevent.php
new file mode 100644
index 0000000..5f61e9c
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Libevent.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Workerman\Worker;
+
+/**
+ * libevent eventloop
+ */
+class Libevent implements EventInterface
+{
+    /**
+     * Event base.
+     *
+     * @var resource
+     */
+    protected $_eventBase = null;
+
+    /**
+     * All listeners for read/write event.
+     *
+     * @var array
+     */
+    protected $_allEvents = array();
+
+    /**
+     * Event listeners of signal.
+     *
+     * @var array
+     */
+    protected $_eventSignal = array();
+
+    /**
+     * All timer event listeners.
+     * [func, args, event, flag, time_interval]
+     *
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * construct
+     */
+    public function __construct()
+    {
+        $this->_eventBase = \event_base_new();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($fd, $flag, $func, $args = array())
+    {
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                $fd_key                      = (int)$fd;
+                $real_flag                   = \EV_SIGNAL | \EV_PERSIST;
+                $this->_eventSignal[$fd_key] = \event_new();
+                if (!\event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) {
+                    return false;
+                }
+                if (!\event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) {
+                    return false;
+                }
+                if (!\event_add($this->_eventSignal[$fd_key])) {
+                    return false;
+                }
+                return true;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $event    = \event_new();
+                $timer_id = (int)$event;
+                if (!\event_set($event, 0, \EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) {
+                    return false;
+                }
+
+                if (!\event_base_set($event, $this->_eventBase)) {
+                    return false;
+                }
+
+                $time_interval = $fd * 1000000;
+                if (!\event_add($event, $time_interval)) {
+                    return false;
+                }
+                $this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval);
+                return $timer_id;
+
+            default :
+                $fd_key    = (int)$fd;
+                $real_flag = $flag === self::EV_READ ? \EV_READ | \EV_PERSIST : \EV_WRITE | \EV_PERSIST;
+
+                $event = \event_new();
+
+                if (!\event_set($event, $fd, $real_flag, $func, null)) {
+                    return false;
+                }
+
+                if (!\event_base_set($event, $this->_eventBase)) {
+                    return false;
+                }
+
+                if (!\event_add($event)) {
+                    return false;
+                }
+
+                $this->_allEvents[$fd_key][$flag] = $event;
+
+                return true;
+        }
+
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int)$fd;
+                if (isset($this->_allEvents[$fd_key][$flag])) {
+                    \event_del($this->_allEvents[$fd_key][$flag]);
+                    unset($this->_allEvents[$fd_key][$flag]);
+                }
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                break;
+            case  self::EV_SIGNAL:
+                $fd_key = (int)$fd;
+                if (isset($this->_eventSignal[$fd_key])) {
+                    \event_del($this->_eventSignal[$fd_key]);
+                    unset($this->_eventSignal[$fd_key]);
+                }
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                // 这里 fd 为timerid 
+                if (isset($this->_eventTimer[$fd])) {
+                    \event_del($this->_eventTimer[$fd][2]);
+                    unset($this->_eventTimer[$fd]);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Timer callback.
+     *
+     * @param mixed $_null1
+     * @param int   $_null2
+     * @param mixed $timer_id
+     */
+    protected function timerCallback($_null1, $_null2, $timer_id)
+    {
+        if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) {
+            \event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]);
+        }
+        try {
+            \call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]);
+        } catch (\Exception $e) {
+            Worker::stopAll(250, $e);
+        } catch (\Error $e) {
+            Worker::stopAll(250, $e);
+        }
+        if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) {
+            $this->del($timer_id, self::EV_TIMER_ONCE);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clearAllTimer()
+    {
+        foreach ($this->_eventTimer as $task_data) {
+            \event_del($task_data[2]);
+        }
+        $this->_eventTimer = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loop()
+    {
+        \event_base_loop($this->_eventBase);
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+        foreach ($this->_eventSignal as $event) {
+            \event_del($event);
+        }
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return \count($this->_eventTimer);
+    }
+}
+
diff --git a/vendor/workerman/workerman/Events/React/Base.php b/vendor/workerman/workerman/Events/React/Base.php
new file mode 100644
index 0000000..bce4f73
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/Base.php
@@ -0,0 +1,264 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+
+use Workerman\Events\EventInterface;
+use React\EventLoop\TimerInterface;
+use React\EventLoop\LoopInterface;
+
+/**
+ * Class StreamSelectLoop
+ * @package Workerman\Events\React
+ */
+class Base implements LoopInterface
+{
+    /**
+     * @var array
+     */
+    protected $_timerIdMap = array();
+
+    /**
+     * @var int
+     */
+    protected $_timerIdIndex = 0;
+
+    /**
+     * @var array
+     */
+    protected $_signalHandlerMap = array();
+
+    /**
+     * @var LoopInterface
+     */
+    protected $_eventLoop = null;
+
+    /**
+     * Base constructor.
+     */
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\StreamSelectLoop();
+    }
+
+    /**
+     * Add event listener to event loop.
+     *
+     * @param int $fd
+     * @param int $flag
+     * @param callable $func
+     * @param array $args
+     * @return bool
+     */
+    public function add($fd, $flag, $func, array $args = array())
+    {
+        $args = (array)$args;
+        switch ($flag) {
+            case EventInterface::EV_READ:
+                return $this->addReadStream($fd, $func);
+            case EventInterface::EV_WRITE:
+                return $this->addWriteStream($fd, $func);
+            case EventInterface::EV_SIGNAL:
+                if (isset($this->_signalHandlerMap[$fd])) {
+                    $this->removeSignal($fd, $this->_signalHandlerMap[$fd]);
+                }
+                $this->_signalHandlerMap[$fd] = $func;
+                return $this->addSignal($fd, $func);
+            case EventInterface::EV_TIMER:
+                $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) {
+                    \call_user_func_array($func, $args);
+                });
+                $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj;
+                return $this->_timerIdIndex;
+            case EventInterface::EV_TIMER_ONCE:
+                $index = ++$this->_timerIdIndex;
+                $timer_obj = $this->addTimer($fd, function() use ($func, $args, $index) {
+                    $this->del($index,EventInterface::EV_TIMER_ONCE);
+                    \call_user_func_array($func, $args);
+                });
+                $this->_timerIdMap[$index] = $timer_obj;
+                return $this->_timerIdIndex;
+        }
+        return false;
+    }
+
+    /**
+     * Remove event listener from event loop.
+     *
+     * @param mixed $fd
+     * @param int   $flag
+     * @return bool
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case EventInterface::EV_READ:
+                return $this->removeReadStream($fd);
+            case EventInterface::EV_WRITE:
+                return $this->removeWriteStream($fd);
+            case EventInterface::EV_SIGNAL:
+                if (!isset($this->_eventLoop[$fd])) {
+                    return false;
+                }
+                $func = $this->_eventLoop[$fd];
+                unset($this->_eventLoop[$fd]);
+                return $this->removeSignal($fd, $func);
+
+            case EventInterface::EV_TIMER:
+            case EventInterface::EV_TIMER_ONCE:
+                if (isset($this->_timerIdMap[$fd])){
+                    $timer_obj = $this->_timerIdMap[$fd];
+                    unset($this->_timerIdMap[$fd]);
+                    $this->cancelTimer($timer_obj);
+                    return true;
+                }
+        }
+        return false;
+    }
+
+
+    /**
+     * Main loop.
+     *
+     * @return void
+     */
+    public function loop()
+    {
+        $this->run();
+    }
+
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return \count($this->_timerIdMap);
+    }
+
+    /**
+     * @param resource $stream
+     * @param callable $listener
+     */
+    public function addReadStream($stream, $listener)
+    {
+        return $this->_eventLoop->addReadStream($stream, $listener);
+    }
+
+    /**
+     * @param resource $stream
+     * @param callable $listener
+     */
+    public function addWriteStream($stream, $listener)
+    {
+        return $this->_eventLoop->addWriteStream($stream, $listener);
+    }
+
+    /**
+     * @param resource $stream
+     */
+    public function removeReadStream($stream)
+    {
+        return $this->_eventLoop->removeReadStream($stream);
+    }
+
+    /**
+     * @param resource $stream
+     */
+    public function removeWriteStream($stream)
+    {
+        return $this->_eventLoop->removeWriteStream($stream);
+    }
+
+    /**
+     * @param float|int $interval
+     * @param callable $callback
+     * @return \React\EventLoop\Timer\Timer|TimerInterface
+     */
+    public function addTimer($interval, $callback)
+    {
+        return $this->_eventLoop->addTimer($interval, $callback);
+    }
+
+    /**
+     * @param float|int $interval
+     * @param callable $callback
+     * @return \React\EventLoop\Timer\Timer|TimerInterface
+     */
+    public function addPeriodicTimer($interval, $callback)
+    {
+        return $this->_eventLoop->addPeriodicTimer($interval, $callback);
+    }
+
+    /**
+     * @param TimerInterface $timer
+     */
+    public function cancelTimer(TimerInterface $timer)
+    {
+        return $this->_eventLoop->cancelTimer($timer);
+    }
+
+    /**
+     * @param callable $listener
+     */
+    public function futureTick($listener)
+    {
+        return $this->_eventLoop->futureTick($listener);
+    }
+
+    /**
+     * @param int $signal
+     * @param callable $listener
+     */
+    public function addSignal($signal, $listener)
+    {
+        return $this->_eventLoop->addSignal($signal, $listener);
+    }
+
+    /**
+     * @param int $signal
+     * @param callable $listener
+     */
+    public function removeSignal($signal, $listener)
+    {
+        return $this->_eventLoop->removeSignal($signal, $listener);
+    }
+
+    /**
+     * Run.
+     */
+    public function run()
+    {
+        return $this->_eventLoop->run();
+    }
+
+    /**
+     * Stop.
+     */
+    public function stop()
+    {
+        return $this->_eventLoop->stop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/React/ExtEventLoop.php b/vendor/workerman/workerman/Events/React/ExtEventLoop.php
new file mode 100644
index 0000000..3dab25b
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/ExtEventLoop.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+
+/**
+ * Class ExtEventLoop
+ * @package Workerman\Events\React
+ */
+class ExtEventLoop extends Base
+{
+
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\ExtEventLoop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php b/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php
new file mode 100644
index 0000000..eb02b35
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/ExtLibEventLoop.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+use Workerman\Events\EventInterface;
+
+/**
+ * Class ExtLibEventLoop
+ * @package Workerman\Events\React
+ */
+class ExtLibEventLoop extends Base
+{
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\ExtLibeventLoop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/React/StreamSelectLoop.php b/vendor/workerman/workerman/Events/React/StreamSelectLoop.php
new file mode 100644
index 0000000..7f5f94b
--- /dev/null
+++ b/vendor/workerman/workerman/Events/React/StreamSelectLoop.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events\React;
+
+/**
+ * Class StreamSelectLoop
+ * @package Workerman\Events\React
+ */
+class StreamSelectLoop extends Base
+{
+    public function __construct()
+    {
+        $this->_eventLoop = new \React\EventLoop\StreamSelectLoop();
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Select.php b/vendor/workerman/workerman/Events/Select.php
new file mode 100644
index 0000000..069ab8b
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Select.php
@@ -0,0 +1,341 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+/**
+ * select eventloop
+ */
+class Select implements EventInterface
+{
+    /**
+     * All listeners for read/write event.
+     *
+     * @var array
+     */
+    public $_allEvents = array();
+
+    /**
+     * Event listeners of signal.
+     *
+     * @var array
+     */
+    public $_signalEvents = array();
+
+    /**
+     * Fds waiting for read event.
+     *
+     * @var array
+     */
+    protected $_readFds = array();
+
+    /**
+     * Fds waiting for write event.
+     *
+     * @var array
+     */
+    protected $_writeFds = array();
+
+    /**
+     * Fds waiting for except event.
+     *
+     * @var array
+     */
+    protected $_exceptFds = array();
+
+    /**
+     * Timer scheduler.
+     * {['data':timer_id, 'priority':run_timestamp], ..}
+     *
+     * @var \SplPriorityQueue
+     */
+    protected $_scheduler = null;
+
+    /**
+     * All timer event listeners.
+     * [[func, args, flag, timer_interval], ..]
+     *
+     * @var array
+     */
+    protected $_eventTimer = array();
+
+    /**
+     * Timer id.
+     *
+     * @var int
+     */
+    protected $_timerId = 1;
+
+    /**
+     * Select timeout.
+     *
+     * @var int
+     */
+    protected $_selectTimeout = 100000000;
+
+    /**
+     * Paired socket channels
+     *
+     * @var array
+     */
+    protected $channel = array();
+
+    /**
+     * Construct.
+     */
+    public function __construct()
+    {
+        // Init SplPriorityQueue.
+        $this->_scheduler = new \SplPriorityQueue();
+        $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($fd, $flag, $func, $args = array())
+    {
+        switch ($flag) {
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $count = $flag === self::EV_READ ? \count($this->_readFds) : \count($this->_writeFds);
+                if ($count >= 1024) {
+                    echo "Warning: system call select exceeded the maximum number of connections 1024, please install event/libevent extension for more connections.\n";
+                } else if (\DIRECTORY_SEPARATOR !== '/' && $count >= 256) {
+                    echo "Warning: system call select exceeded the maximum number of connections 256.\n";
+                }
+                $fd_key                           = (int)$fd;
+                $this->_allEvents[$fd_key][$flag] = array($func, $fd);
+                if ($flag === self::EV_READ) {
+                    $this->_readFds[$fd_key] = $fd;
+                } else {
+                    $this->_writeFds[$fd_key] = $fd;
+                }
+                break;
+            case self::EV_EXCEPT:
+                $fd_key = (int)$fd;
+                $this->_allEvents[$fd_key][$flag] = array($func, $fd);
+                $this->_exceptFds[$fd_key] = $fd;
+                break;
+            case self::EV_SIGNAL:
+                // Windows not support signal.
+                if(\DIRECTORY_SEPARATOR !== '/') {
+                    return false;
+                }
+                $fd_key                              = (int)$fd;
+                $this->_signalEvents[$fd_key][$flag] = array($func, $fd);
+                \pcntl_signal($fd, array($this, 'signalHandler'));
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $timer_id = $this->_timerId++;
+                $run_time = \microtime(true) + $fd;
+                $this->_scheduler->insert($timer_id, -$run_time);
+                $this->_eventTimer[$timer_id] = array($func, (array)$args, $flag, $fd);
+                $select_timeout = ($run_time - \microtime(true)) * 1000000;
+                $select_timeout = $select_timeout <= 0 ? 1 : $select_timeout;
+                if( $this->_selectTimeout > $select_timeout ){ 
+                    $this->_selectTimeout = (int) $select_timeout;   
+                }  
+                return $timer_id;
+        }
+
+        return true;
+    }
+
+    /**
+     * Signal handler.
+     *
+     * @param int $signal
+     */
+    public function signalHandler($signal)
+    {
+        \call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function del($fd, $flag)
+    {
+        $fd_key = (int)$fd;
+        switch ($flag) {
+            case self::EV_READ:
+                unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]);
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                return true;
+            case self::EV_WRITE:
+                unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]);
+                if (empty($this->_allEvents[$fd_key])) {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                return true;
+            case self::EV_EXCEPT:
+                unset($this->_allEvents[$fd_key][$flag], $this->_exceptFds[$fd_key]);
+                if(empty($this->_allEvents[$fd_key]))
+                {
+                    unset($this->_allEvents[$fd_key]);
+                }
+                return true;
+            case self::EV_SIGNAL:
+                if(\DIRECTORY_SEPARATOR !== '/') {
+                    return false;
+                }
+                unset($this->_signalEvents[$fd_key]);
+                \pcntl_signal($fd, SIG_IGN);
+                break;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE;
+                unset($this->_eventTimer[$fd_key]);
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Tick for timer.
+     *
+     * @return void
+     */
+    protected function tick()
+    {
+        while (!$this->_scheduler->isEmpty()) {
+            $scheduler_data       = $this->_scheduler->top();
+            $timer_id             = $scheduler_data['data'];
+            $next_run_time        = -$scheduler_data['priority'];
+            $time_now             = \microtime(true);
+            $this->_selectTimeout = (int) (($next_run_time - $time_now) * 1000000);
+            if ($this->_selectTimeout <= 0) {
+                $this->_scheduler->extract();
+
+                if (!isset($this->_eventTimer[$timer_id])) {
+                    continue;
+                }
+
+                // [func, args, flag, timer_interval]
+                $task_data = $this->_eventTimer[$timer_id];
+                if ($task_data[2] === self::EV_TIMER) {
+                    $next_run_time = $time_now + $task_data[3];
+                    $this->_scheduler->insert($timer_id, -$next_run_time);
+                }
+                \call_user_func_array($task_data[0], $task_data[1]);
+                if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) {
+                    $this->del($timer_id, self::EV_TIMER_ONCE);
+                }
+                continue;
+            }
+            return;
+        }
+        $this->_selectTimeout = 100000000;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clearAllTimer()
+    {
+        $this->_scheduler = new \SplPriorityQueue();
+        $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
+        $this->_eventTimer = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function loop()
+    {
+        while (1) {
+            if(\DIRECTORY_SEPARATOR === '/') {
+                // Calls signal handlers for pending signals
+                \pcntl_signal_dispatch();
+            }
+
+            $read   = $this->_readFds;
+            $write  = $this->_writeFds;
+            $except = $this->_exceptFds;
+            $ret    = false;
+
+            if ($read || $write || $except) {
+                // Waiting read/write/signal/timeout events.
+                try {
+                    $ret = @stream_select($read, $write, $except, 0, $this->_selectTimeout);
+                } catch (\Exception $e) {} catch (\Error $e) {}
+
+            } else {
+                $this->_selectTimeout >= 1 && usleep($this->_selectTimeout);
+                $ret = false;
+            }
+
+
+            if (!$this->_scheduler->isEmpty()) {
+                $this->tick();
+            }
+
+            if (!$ret) {
+                continue;
+            }
+
+            if ($read) {
+                foreach ($read as $fd) {
+                    $fd_key = (int)$fd;
+                    if (isset($this->_allEvents[$fd_key][self::EV_READ])) {
+                        \call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0],
+                            array($this->_allEvents[$fd_key][self::EV_READ][1]));
+                    }
+                }
+            }
+
+            if ($write) {
+                foreach ($write as $fd) {
+                    $fd_key = (int)$fd;
+                    if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) {
+                        \call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0],
+                            array($this->_allEvents[$fd_key][self::EV_WRITE][1]));
+                    }
+                }
+            }
+
+            if($except) {
+                foreach($except as $fd) {
+                    $fd_key = (int) $fd;
+                    if(isset($this->_allEvents[$fd_key][self::EV_EXCEPT])) {
+                        \call_user_func_array($this->_allEvents[$fd_key][self::EV_EXCEPT][0],
+                            array($this->_allEvents[$fd_key][self::EV_EXCEPT][1]));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Destroy loop.
+     *
+     * @return void
+     */
+    public function destroy()
+    {
+
+    }
+
+    /**
+     * Get timer count.
+     *
+     * @return integer
+     */
+    public function getTimerCount()
+    {
+        return \count($this->_eventTimer);
+    }
+}
diff --git a/vendor/workerman/workerman/Events/Swoole.php b/vendor/workerman/workerman/Events/Swoole.php
new file mode 100644
index 0000000..fcd7472
--- /dev/null
+++ b/vendor/workerman/workerman/Events/Swoole.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    Ares<aresrr#qq.com>
+ * @link      http://www.workerman.net/
+ * @link      https://github.com/ares333/Workerman
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Events;
+
+use Workerman\Worker;
+use Swoole\Event;
+use Swoole\Timer;
+
+class Swoole implements EventInterface
+{
+
+    protected $_timer = array();
+
+    protected $_timerOnceMap = array();
+
+    protected $mapId = 0;
+
+    protected $_fd = array();
+
+    // milisecond
+    public static $signalDispatchInterval = 500;
+
+    protected $_hasSignal = false;
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::add()
+     */
+    public function add($fd, $flag, $func, $args = array())
+    {
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                $res = \pcntl_signal($fd, $func, false);
+                if (! $this->_hasSignal && $res) {
+                    Timer::tick(static::$signalDispatchInterval,
+                        function () {
+                            \pcntl_signal_dispatch();
+                        });
+                    $this->_hasSignal = true;
+                }
+                return $res;
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                $method = self::EV_TIMER === $flag ? 'tick' : 'after';
+                if ($this->mapId > \PHP_INT_MAX) {
+                    $this->mapId = 0;
+                }
+                $mapId = $this->mapId++;
+                $t = (int)($fd * 1000);
+                if ($t < 1) {
+                   $t = 1;   
+                }
+                $timer_id = Timer::$method($t,
+                    function ($timer_id = null) use ($func, $args, $mapId) {
+                        try {
+                            \call_user_func_array($func, (array)$args);
+                        } catch (\Exception $e) {
+                            Worker::stopAll(250, $e);
+                        } catch (\Error $e) {
+                            Worker::stopAll(250, $e);
+                        }
+                        // EV_TIMER_ONCE
+                        if (! isset($timer_id)) {
+                            // may be deleted in $func
+                            if (\array_key_exists($mapId, $this->_timerOnceMap)) {
+                                $timer_id = $this->_timerOnceMap[$mapId];
+                                unset($this->_timer[$timer_id],
+                                    $this->_timerOnceMap[$mapId]);
+                            }
+                        }
+                    });
+                if ($flag === self::EV_TIMER_ONCE) {
+                    $this->_timerOnceMap[$mapId] = $timer_id;
+                    $this->_timer[$timer_id] = $mapId;
+                } else {
+                    $this->_timer[$timer_id] = null;
+                }
+                return $timer_id;
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int) $fd;
+                if (! isset($this->_fd[$fd_key])) {
+                    if ($flag === self::EV_READ) {
+                        $res = Event::add($fd, $func, null, SWOOLE_EVENT_READ);
+                        $fd_type = SWOOLE_EVENT_READ;
+                    } else {
+                        $res = Event::add($fd, null, $func, SWOOLE_EVENT_WRITE);
+                        $fd_type = SWOOLE_EVENT_WRITE;
+                    }
+                    if ($res) {
+                        $this->_fd[$fd_key] = $fd_type;
+                    }
+                } else {
+                    $fd_val = $this->_fd[$fd_key];
+                    $res = true;
+                    if ($flag === self::EV_READ) {
+                        if (($fd_val & SWOOLE_EVENT_READ) !== SWOOLE_EVENT_READ) {
+                            $res = Event::set($fd, $func, null,
+                                SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
+                            $this->_fd[$fd_key] |= SWOOLE_EVENT_READ;
+                        }
+                    } else {
+                        if (($fd_val & SWOOLE_EVENT_WRITE) !== SWOOLE_EVENT_WRITE) {
+                            $res = Event::set($fd, null, $func,
+                                SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
+                            $this->_fd[$fd_key] |= SWOOLE_EVENT_WRITE;
+                        }
+                    }
+                }
+                return $res;
+        }
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::del()
+     */
+    public function del($fd, $flag)
+    {
+        switch ($flag) {
+            case self::EV_SIGNAL:
+                return \pcntl_signal($fd, SIG_IGN, false);
+            case self::EV_TIMER:
+            case self::EV_TIMER_ONCE:
+                // already remove in EV_TIMER_ONCE callback.
+                if (! \array_key_exists($fd, $this->_timer)) {
+                    return true;
+                }
+                $res = Timer::clear($fd);
+                if ($res) {
+                    $mapId = $this->_timer[$fd];
+                    if (isset($mapId)) {
+                        unset($this->_timerOnceMap[$mapId]);
+                    }
+                    unset($this->_timer[$fd]);
+                }
+                return $res;
+            case self::EV_READ:
+            case self::EV_WRITE:
+                $fd_key = (int) $fd;
+                if (isset($this->_fd[$fd_key])) {
+                    $fd_val = $this->_fd[$fd_key];
+                    if ($flag === self::EV_READ) {
+                        $flag_remove = ~ SWOOLE_EVENT_READ;
+                    } else {
+                        $flag_remove = ~ SWOOLE_EVENT_WRITE;
+                    }
+                    $fd_val &= $flag_remove;
+                    if (0 === $fd_val) {
+                        $res = Event::del($fd);
+                        if ($res) {
+                            unset($this->_fd[$fd_key]);
+                        }
+                    } else {
+                        $res = Event::set($fd, null, null, $fd_val);
+                        if ($res) {
+                            $this->_fd[$fd_key] = $fd_val;
+                        }
+                    }
+                } else {
+                    $res = true;
+                }
+                return $res;
+        }
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::clearAllTimer()
+     */
+    public function clearAllTimer()
+    {
+        foreach (array_keys($this->_timer) as $v) {
+            Timer::clear($v);
+        }
+        $this->_timer = array();
+        $this->_timerOnceMap = array();
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::loop()
+     */
+    public function loop()
+    {
+        Event::wait();
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::destroy()
+     */
+    public function destroy()
+    {
+        Event::exit();
+        posix_kill(posix_getpid(), SIGINT);
+    }
+
+    /**
+     *
+     * {@inheritdoc}
+     *
+     * @see \Workerman\Events\EventInterface::getTimerCount()
+     */
+    public function getTimerCount()
+    {
+        return \count($this->_timer);
+    }
+}
diff --git a/vendor/workerman/workerman/Lib/Constants.php b/vendor/workerman/workerman/Lib/Constants.php
new file mode 100644
index 0000000..f5e2424
--- /dev/null
+++ b/vendor/workerman/workerman/Lib/Constants.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ *
+ * @link      http://www.workerman.net/
+ */
+
+// Pcre.jit is not stable, temporarily disabled.
+ini_set('pcre.jit', 0);
+
+// For onError callback.
+const WORKERMAN_CONNECT_FAIL = 1;
+// For onError callback.
+const WORKERMAN_SEND_FAIL = 2;
+
+// Define OS Type
+const OS_TYPE_LINUX   = 'linux';
+const OS_TYPE_WINDOWS = 'windows';
+
+// Compatible with php7
+if (!class_exists('Error')) {
+    class Error extends Exception
+    {
+    }
+}
+
+if (!interface_exists('SessionHandlerInterface')) {
+    interface SessionHandlerInterface {
+        public function close();
+        public function destroy($session_id);
+        public function gc($maxlifetime);
+        public function open($save_path ,$session_name);
+        public function read($session_id);
+        public function write($session_id , $session_data);
+    }
+}
diff --git a/vendor/workerman/workerman/Lib/Timer.php b/vendor/workerman/workerman/Lib/Timer.php
new file mode 100644
index 0000000..b110051
--- /dev/null
+++ b/vendor/workerman/workerman/Lib/Timer.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Lib;
+
+/**
+ * Do not use Workerman\Lib\Timer.
+ * Please use Workerman\Timer.
+ * This class is only used for compatibility with workerman 3.*
+ * @package Workerman\Lib
+ */
+class Timer extends \Workerman\Timer {}
\ No newline at end of file
diff --git a/vendor/workerman/workerman/MIT-LICENSE.txt b/vendor/workerman/workerman/MIT-LICENSE.txt
new file mode 100644
index 0000000..fd6b1c8
--- /dev/null
+++ b/vendor/workerman/workerman/MIT-LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2009-2015 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/workerman/contributors)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/workerman/workerman/Protocols/Frame.php b/vendor/workerman/workerman/Protocols/Frame.php
new file mode 100644
index 0000000..26b04de
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Frame.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\TcpConnection;
+
+/**
+ * Frame Protocol.
+ */
+class Frame
+{
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string        $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function input($buffer, TcpConnection $connection)
+    {
+        if (\strlen($buffer) < 4) {
+            return 0;
+        }
+        $unpack_data = \unpack('Ntotal_length', $buffer);
+        return $unpack_data['total_length'];
+    }
+
+    /**
+     * Decode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function decode($buffer)
+    {
+        return \substr($buffer, 4);
+    }
+
+    /**
+     * Encode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function encode($buffer)
+    {
+        $total_length = 4 + \strlen($buffer);
+        return \pack('N', $total_length) . $buffer;
+    }
+}
diff --git a/vendor/workerman/workerman/Protocols/Http.php b/vendor/workerman/workerman/Protocols/Http.php
new file mode 100644
index 0000000..be3599f
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http.php
@@ -0,0 +1,330 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\TcpConnection;
+use Workerman\Protocols\Http\Request;
+use Workerman\Protocols\Http\Response;
+use Workerman\Protocols\Websocket;
+use Workerman\Worker;
+
+/**
+ * Class Http.
+ * @package Workerman\Protocols
+ */
+class Http
+{
+    /**
+     * Request class name.
+     *
+     * @var string
+     */
+    protected static $_requestClass = 'Workerman\Protocols\Http\Request';
+
+    /**
+     * Session name.
+     *
+     * @var string
+     */
+    protected static $_sessionName = 'PHPSID';
+
+    /**
+     * Upload tmp dir.
+     *
+     * @var string
+     */
+    protected static $_uploadTmpDir = '';
+
+    /**
+     * Open cache.
+     *
+     * @var bool.
+     */
+    protected static $_enableCache = true;
+
+    /**
+     * Get or set session name.
+     *
+     * @param string|null $name
+     * @return string
+     */
+    public static function sessionName($name = null)
+    {
+        if ($name !== null && $name !== '') {
+            static::$_sessionName = (string)$name;
+        }
+        return static::$_sessionName;
+    }
+
+    /**
+     * Get or set the request class name.
+     *
+     * @param string|null $class_name
+     * @return string
+     */
+    public static function requestClass($class_name = null)
+    {
+        if ($class_name) {
+            static::$_requestClass = $class_name;
+        }
+        return static::$_requestClass;
+    }
+
+    /**
+     * Enable or disable Cache.
+     *
+     * @param mixed $value
+     */
+    public static function enableCache($value)
+    {
+        static::$_enableCache = (bool)$value;
+    }
+
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string $recv_buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function input($recv_buffer, TcpConnection $connection)
+    {
+        static $input = array();
+        if (!isset($recv_buffer[512]) && isset($input[$recv_buffer])) {
+            return $input[$recv_buffer];
+        }
+        $crlf_pos = \strpos($recv_buffer, "\r\n\r\n");
+        if (false === $crlf_pos) {
+            // Judge whether the package length exceeds the limit.
+            if ($recv_len = \strlen($recv_buffer) >= 16384) {
+                $connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n");
+                return 0;
+            }
+            return 0;
+        }
+
+        $head_len = $crlf_pos + 4;
+        $method = \strstr($recv_buffer, ' ', true);
+
+        if ($method === 'GET' || $method === 'OPTIONS' || $method === 'HEAD' || $method === 'DELETE') {
+            if (!isset($recv_buffer[512])) {
+                $input[$recv_buffer] = $head_len;
+                if (\count($input) > 512) {
+                    unset($input[key($input)]);
+                }
+            }
+            return $head_len;
+        } else if ($method !== 'POST' && $method !== 'PUT' && $method !== 'PATCH') {
+            $connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
+            return 0;
+        }
+
+        $header = \substr($recv_buffer, 0, $crlf_pos);
+        $length = false;
+        if ($pos = \strpos($header, "\r\nContent-Length: ")) {
+            $length = $head_len + (int)\substr($header, $pos + 18, 10);
+        } else if (\preg_match("/\r\ncontent-length: ?(\d+)/i", $header, $match)) {
+            $length = $head_len + $match[1];
+        }
+
+        if ($length !== false) {
+            if (!isset($recv_buffer[512])) {
+                $input[$recv_buffer] = $length;
+                if (\count($input) > 512) {
+                    unset($input[key($input)]);
+                }
+            }
+            if ($length > $connection->maxPackageSize) {
+                $connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n");
+                return 0;
+            }
+            return $length;
+        }
+
+        $connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
+        return 0;
+    }
+
+    /**
+     * Http decode.
+     *
+     * @param string $recv_buffer
+     * @param TcpConnection $connection
+     * @return \Workerman\Protocols\Http\Request
+     */
+    public static function decode($recv_buffer, TcpConnection $connection)
+    {
+        static $requests = array();
+        $cacheable = static::$_enableCache && !isset($recv_buffer[512]);
+        if (true === $cacheable && isset($requests[$recv_buffer])) {
+            $request = $requests[$recv_buffer];
+            $request->connection = $connection;
+            $connection->__request = $request;
+            $request->properties = array();
+            return $request;
+        }
+        $request = new static::$_requestClass($recv_buffer);
+        $request->connection = $connection;
+        $connection->__request = $request;
+        if (true === $cacheable) {
+            $requests[$recv_buffer] = $request;
+            if (\count($requests) > 512) {
+                unset($requests[key($requests)]);
+            }
+        }
+        return $request;
+    }
+
+    /**
+     * Http encode.
+     *
+     * @param string|Response $response
+     * @param TcpConnection $connection
+     * @return string
+     */
+    public static function encode($response, TcpConnection $connection)
+    {
+        if (isset($connection->__request)) {
+            $connection->__request->session = null;
+            $connection->__request->connection = null;
+            $connection->__request = null;
+        }
+        if (!\is_object($response)) {
+            $ext_header = '';
+            if (isset($connection->__header)) {
+                foreach ($connection->__header as $name => $value) {
+                    if (\is_array($value)) {
+                        foreach ($value as $item) {
+                            $ext_header = "$name: $item\r\n";
+                        }
+                    } else {
+                        $ext_header = "$name: $value\r\n";
+                    }
+                }
+                unset($connection->__header);
+            }
+            $body_len = \strlen($response);
+            return "HTTP/1.1 200 OK\r\nServer: workerman\r\n{$ext_header}Connection: keep-alive\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\n\r\n$response";
+        }
+
+        if (isset($connection->__header)) {
+            $response->withHeaders($connection->__header);
+            unset($connection->__header);
+        }
+
+        if (isset($response->file)) {
+            $file = $response->file['file'];
+            $offset = $response->file['offset'];
+            $length = $response->file['length'];
+            $file_size = (int)\filesize($file);
+            $body_len = $length > 0 ? $length : $file_size - $offset;
+            $response->withHeaders(array(
+                'Content-Length' => $body_len,
+                'Accept-Ranges'  => 'bytes',
+            ));
+            if ($offset || $length) {
+                $offset_end = $offset + $body_len - 1;
+                $response->header('Content-Range', "bytes $offset-$offset_end/$file_size");
+            }
+            if ($body_len < 2 * 1024 * 1024) {
+                $connection->send((string)$response . file_get_contents($file, false, null, $offset, $body_len), true);
+                return '';
+            }
+            $handler = \fopen($file, 'r');
+            if (false === $handler) {
+                $connection->close(new Response(403, null, '403 Forbidden'));
+                return '';
+            }
+            $connection->send((string)$response, true);
+            static::sendStream($connection, $handler, $offset, $length);
+            return '';
+        }
+
+        return (string)$response;
+    }
+
+    /**
+     * Send remainder of a stream to client.
+     *
+     * @param TcpConnection $connection
+     * @param resource $handler
+     * @param int $offset
+     * @param int $length
+     */
+    protected static function sendStream(TcpConnection $connection, $handler, $offset = 0, $length = 0)
+    {
+        $connection->bufferFull = false;
+        if ($offset !== 0) {
+            \fseek($handler, $offset);
+        }
+        $offset_end = $offset + $length;
+        // Read file content from disk piece by piece and send to client.
+        $do_write = function () use ($connection, $handler, $length, $offset_end) {
+            // Send buffer not full.
+            while ($connection->bufferFull === false) {
+                // Read from disk.
+                $size = 1024 * 1024;
+                if ($length !== 0) {
+                    $tell = \ftell($handler);
+                    $remain_size = $offset_end - $tell;
+                    if ($remain_size <= 0) {
+                        fclose($handler);
+                        $connection->onBufferDrain = null;
+                        return;
+                    }
+                    $size = $remain_size > $size ? $size : $remain_size;
+                }
+
+                $buffer = \fread($handler, $size);
+                // Read eof.
+                if ($buffer === '' || $buffer === false) {
+                    fclose($handler);
+                    $connection->onBufferDrain = null;
+                    return;
+                }
+                $connection->send($buffer, true);
+            }
+        };
+        // Send buffer full.
+        $connection->onBufferFull = function ($connection) {
+            $connection->bufferFull = true;
+        };
+        // Send buffer drain.
+        $connection->onBufferDrain = function ($connection) use ($do_write) {
+            $connection->bufferFull = false;
+            $do_write();
+        };
+        $do_write();
+    }
+
+    /**
+     * Set or get uploadTmpDir.
+     *
+     * @return bool|string
+     */
+    public static function uploadTmpDir($dir = null)
+    {
+        if (null !== $dir) {
+            static::$_uploadTmpDir = $dir;
+        }
+        if (static::$_uploadTmpDir === '') {
+            if ($upload_tmp_dir = \ini_get('upload_tmp_dir')) {
+                static::$_uploadTmpDir = $upload_tmp_dir;
+            } else if ($upload_tmp_dir = \sys_get_temp_dir()) {
+                static::$_uploadTmpDir = $upload_tmp_dir;
+            }
+        }
+        return static::$_uploadTmpDir;
+    }
+}
diff --git a/vendor/workerman/workerman/Protocols/Http/Chunk.php b/vendor/workerman/workerman/Protocols/Http/Chunk.php
new file mode 100644
index 0000000..ab06a9c
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/Chunk.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http;
+
+
+/**
+ * Class Chunk
+ * @package Workerman\Protocols\Http
+ */
+class Chunk
+{
+    /**
+     * Chunk buffer.
+     *
+     * @var string
+     */
+    protected $_buffer = null;
+
+    /**
+     * Chunk constructor.
+     * @param string $buffer
+     */
+    public function __construct($buffer)
+    {
+        $this->_buffer = $buffer;
+    }
+
+    /**
+     * __toString
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return \dechex(\strlen($this->_buffer))."\r\n$this->_buffer\r\n";
+    }
+}
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Protocols/Http/Request.php b/vendor/workerman/workerman/Protocols/Http/Request.php
new file mode 100644
index 0000000..301c67a
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/Request.php
@@ -0,0 +1,665 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http;
+
+use Workerman\Connection\TcpConnection;
+use Workerman\Protocols\Http\Session;
+use Workerman\Protocols\Http;
+use Workerman\Worker;
+
+/**
+ * Class Request
+ * @package Workerman\Protocols\Http
+ */
+class Request
+{
+    /**
+     * Connection.
+     *
+     * @var TcpConnection
+     */
+    public $connection = null;
+
+    /**
+     * Session instance.
+     *
+     * @var Session
+     */
+    public $session = null;
+
+    /**
+     * Properties.
+     *
+     * @var array
+     */
+    public $properties = array();
+
+    /**
+     * Http buffer.
+     *
+     * @var string
+     */
+    protected $_buffer = null;
+
+    /**
+     * Request data.
+     *
+     * @var array
+     */
+    protected $_data = null;
+
+    /**
+     * Header cache.
+     *
+     * @var array
+     */
+    protected static $_headerCache = array();
+
+    /**
+     * Get cache.
+     *
+     * @var array
+     */
+    protected static $_getCache = array();
+
+    /**
+     * Post cache.
+     *
+     * @var array
+     */
+    protected static $_postCache = array();
+
+    /**
+     * Enable cache.
+     *
+     * @var bool
+     */
+    protected static $_enableCache = true;
+
+
+    /**
+     * Request constructor.
+     *
+     * @param string $buffer
+     */
+    public function __construct($buffer)
+    {
+        $this->_buffer = $buffer;
+    }
+
+    /**
+     * $_GET.
+     *
+     * @param string|null $name
+     * @param mixed|null $default
+     * @return mixed|null
+     */
+    public function get($name = null, $default = null)
+    {
+        if (!isset($this->_data['get'])) {
+            $this->parseGet();
+        }
+        if (null === $name) {
+            return $this->_data['get'];
+        }
+        return isset($this->_data['get'][$name]) ? $this->_data['get'][$name] : $default;
+    }
+
+    /**
+     * $_POST.
+     *
+     * @param string|null $name
+     * @param mixed|null $default
+     * @return mixed|null
+     */
+    public function post($name = null, $default = null)
+    {
+        if (!isset($this->_data['post'])) {
+            $this->parsePost();
+        }
+        if (null === $name) {
+            return $this->_data['post'];
+        }
+        return isset($this->_data['post'][$name]) ? $this->_data['post'][$name] : $default;
+    }
+
+    /**
+     * Get header item by name.
+     *
+     * @param string|null $name
+     * @param mixed|null $default
+     * @return array|string|null
+     */
+    public function header($name = null, $default = null)
+    {
+        if (!isset($this->_data['headers'])) {
+            $this->parseHeaders();
+        }
+        if (null === $name) {
+            return $this->_data['headers'];
+        }
+        $name = \strtolower($name);
+        return isset($this->_data['headers'][$name]) ? $this->_data['headers'][$name] : $default;
+    }
+
+    /**
+     * Get cookie item by name.
+     *
+     * @param string|null $name
+     * @param mixed|null $default
+     * @return array|string|null
+     */
+    public function cookie($name = null, $default = null)
+    {
+        if (!isset($this->_data['cookie'])) {
+            $this->_data['cookie'] = array();
+            \parse_str(\preg_replace('/; ?/', '&', $this->header('cookie', '')), $this->_data['cookie']);
+        }
+        if ($name === null) {
+            return $this->_data['cookie'];
+        }
+        return isset($this->_data['cookie'][$name]) ? $this->_data['cookie'][$name] : $default;
+    }
+
+    /**
+     * Get upload files.
+     *
+     * @param string|null $name
+     * @return array|null
+     */
+    public function file($name = null)
+    {
+        if (!isset($this->_data['files'])) {
+            $this->parsePost();
+        }
+        if (null === $name) {
+            return $this->_data['files'];
+        }
+        return isset($this->_data['files'][$name]) ? $this->_data['files'][$name] : null;
+    }
+
+    /**
+     * Get method.
+     *
+     * @return string
+     */
+    public function method()
+    {
+        if (!isset($this->_data['method'])) {
+            $this->parseHeadFirstLine();
+        }
+        return $this->_data['method'];
+    }
+
+    /**
+     * Get http protocol version.
+     *
+     * @return string
+     */
+    public function protocolVersion()
+    {
+        if (!isset($this->_data['protocolVersion'])) {
+            $this->parseProtocolVersion();
+        }
+        return $this->_data['protocolVersion'];
+    }
+
+    /**
+     * Get host.
+     *
+     * @param bool $without_port
+     * @return string
+     */
+    public function host($without_port = false)
+    {
+        $host = $this->header('host');
+        if ($without_port && $pos = \strpos($host, ':')) {
+            return \substr($host, 0, $pos);
+        }
+        return $host;
+    }
+
+    /**
+     * Get uri.
+     *
+     * @return mixed
+     */
+    public function uri()
+    {
+        if (!isset($this->_data['uri'])) {
+            $this->parseHeadFirstLine();
+        }
+        return $this->_data['uri'];
+    }
+
+    /**
+     * Get path.
+     *
+     * @return mixed
+     */
+    public function path()
+    {
+        if (!isset($this->_data['path'])) {
+            $this->_data['path'] = (string)\parse_url($this->uri(), PHP_URL_PATH);
+        }
+        return $this->_data['path'];
+    }
+
+    /**
+     * Get query string.
+     *
+     * @return mixed
+     */
+    public function queryString()
+    {
+        if (!isset($this->_data['query_string'])) {
+            $this->_data['query_string'] = (string)\parse_url($this->uri(), PHP_URL_QUERY);
+        }
+        return $this->_data['query_string'];
+    }
+
+    /**
+     * Get session.
+     *
+     * @return bool|\Workerman\Protocols\Http\Session
+     */
+    public function session()
+    {
+        if ($this->session === null) {
+            $session_id = $this->sessionId();
+            if ($session_id === false) {
+                return false;
+            }
+            $this->session = new Session($session_id);
+        }
+        return $this->session;
+    }
+
+    /**
+     * Get session id.
+     *
+     * @return bool|mixed
+     */
+    public function sessionId()
+    {
+        if (!isset($this->sid)) {
+            $session_name = Http::sessionName();
+            $sid = $this->cookie($session_name);
+            if ($sid === '' || $sid === null) {
+                if ($this->connection === null) {
+                    Worker::safeEcho('Request->session() fail, header already send');
+                    return false;
+                }
+                $sid = static::createSessionId();
+                $cookie_params = \session_get_cookie_params();
+                $this->connection->__header['Set-Cookie'] = array($session_name . '=' . $sid
+                    . (empty($cookie_params['domain']) ? '' : '; Domain=' . $cookie_params['domain'])
+                    . (empty($cookie_params['lifetime']) ? '' : '; Max-Age=' . ($cookie_params['lifetime'] + \time()))
+                    . (empty($cookie_params['path']) ? '' : '; Path=' . $cookie_params['path'])
+                    . (empty($cookie_params['samesite']) ? '' : '; SameSite=' . $cookie_params['samesite'])
+                    . (!$cookie_params['secure'] ? '' : '; Secure')
+                    . (!$cookie_params['httponly'] ? '' : '; HttpOnly'));
+            }
+            $this->sid = $sid;
+        }
+        return $this->sid;
+    }
+
+    /**
+     * Get http raw head.
+     *
+     * @return string
+     */
+    public function rawHead()
+    {
+        if (!isset($this->_data['head'])) {
+            $this->_data['head'] = \strstr($this->_buffer, "\r\n\r\n", true);
+        }
+        return $this->_data['head'];
+    }
+
+    /**
+     * Get http raw body.
+     *
+     * @return string
+     */
+    public function rawBody()
+    {
+        return \substr($this->_buffer, \strpos($this->_buffer, "\r\n\r\n") + 4);
+    }
+
+    /**
+     * Get raw buffer.
+     *
+     * @return string
+     */
+    public function rawBuffer()
+    {
+        return $this->_buffer;
+    }
+
+    /**
+     * Enable or disable cache.
+     *
+     * @param mixed $value
+     */
+    public static function enableCache($value)
+    {
+        static::$_enableCache = (bool)$value;
+    }
+
+    /**
+     * Parse first line of http header buffer.
+     *
+     * @return void
+     */
+    protected function parseHeadFirstLine()
+    {
+        $first_line = \strstr($this->_buffer, "\r\n", true);
+        $tmp = \explode(' ', $first_line, 3);
+        $this->_data['method'] = $tmp[0];
+        $this->_data['uri'] = isset($tmp[1]) ? $tmp[1] : '/';
+    }
+
+    /**
+     * Parse protocol version.
+     *
+     * @return void
+     */
+    protected function parseProtocolVersion()
+    {
+        $first_line = \strstr($this->_buffer, "\r\n", true);
+        $protoco_version = substr(\strstr($first_line, 'HTTP/'), 5);
+        $this->_data['protocolVersion'] = $protoco_version ? $protoco_version : '1.0';
+    }
+
+    /**
+     * Parse headers.
+     *
+     * @return void
+     */
+    protected function parseHeaders()
+    {
+        $this->_data['headers'] = array();
+        $raw_head = $this->rawHead();
+        $end_line_position = \strpos($raw_head, "\r\n");
+        if ($end_line_position === false) {
+            return;
+        }
+        $head_buffer = \substr($raw_head, $end_line_position + 2);
+        $cacheable = static::$_enableCache && !isset($head_buffer[2048]);
+        if ($cacheable && isset(static::$_headerCache[$head_buffer])) {
+            $this->_data['headers'] = static::$_headerCache[$head_buffer];
+            return;
+        }
+        $head_data = \explode("\r\n", $head_buffer);
+        foreach ($head_data as $content) {
+            if (false !== \strpos($content, ':')) {
+                list($key, $value) = \explode(':', $content, 2);
+                $key = \strtolower($key);
+                $value = \ltrim($value);
+            } else {
+                $key = \strtolower($content);
+                $value = '';
+            }
+            if (isset($this->_data['headers'][$key])) {
+                $this->_data['headers'][$key] = "{$this->_data['headers'][$key]},$value";
+            } else {
+                $this->_data['headers'][$key] = $value;
+            }
+        }
+        if ($cacheable) {
+            static::$_headerCache[$head_buffer] = $this->_data['headers'];
+            if (\count(static::$_headerCache) > 128) {
+                unset(static::$_headerCache[key(static::$_headerCache)]);
+            }
+        }
+    }
+
+    /**
+     * Parse head.
+     *
+     * @return void
+     */
+    protected function parseGet()
+    {
+        $query_string = $this->queryString();
+        $this->_data['get'] = array();
+        if ($query_string === '') {
+            return;
+        }
+        $cacheable = static::$_enableCache && !isset($query_string[1024]);
+        if ($cacheable && isset(static::$_getCache[$query_string])) {
+            $this->_data['get'] = static::$_getCache[$query_string];
+            return;
+        }
+        \parse_str($query_string, $this->_data['get']);
+        if ($cacheable) {
+            static::$_getCache[$query_string] = $this->_data['get'];
+            if (\count(static::$_getCache) > 256) {
+                unset(static::$_getCache[key(static::$_getCache)]);
+            }
+        }
+    }
+
+    /**
+     * Parse post.
+     *
+     * @return void
+     */
+    protected function parsePost()
+    {
+        $body_buffer = $this->rawBody();
+        $this->_data['post'] = $this->_data['files'] = array();
+        if ($body_buffer === '') {
+            return;
+        }
+        $cacheable = static::$_enableCache && !isset($body_buffer[1024]);
+        if ($cacheable && isset(static::$_postCache[$body_buffer])) {
+            $this->_data['post'] = static::$_postCache[$body_buffer];
+            return;
+        }
+        $content_type = $this->header('content-type', '');
+        if (\preg_match('/boundary="?(\S+)"?/', $content_type, $match)) {
+            $http_post_boundary = '--' . $match[1];
+            $this->parseUploadFiles($http_post_boundary);
+            return;
+        }
+        if (\preg_match('/\bjson\b/i', $content_type)) {
+            $this->_data['post'] = (array) json_decode($body_buffer, true);
+        } else {
+            \parse_str($body_buffer, $this->_data['post']);
+        }
+        if ($cacheable) {
+            static::$_postCache[$body_buffer] = $this->_data['post'];
+            if (\count(static::$_postCache) > 256) {
+                unset(static::$_postCache[key(static::$_postCache)]);
+            }
+        }
+    }
+
+    /**
+     * Parse upload files.
+     *
+     * @param string $http_post_boundary
+     * @return void
+     */
+    protected function parseUploadFiles($http_post_boundary)
+    {
+        $http_post_boundary  = \trim($http_post_boundary, '"');
+        $http_body           = $this->rawBody();
+        $http_body           = \substr($http_body, 0, \strlen($http_body) - (\strlen($http_post_boundary) + 4));
+        $boundary_data_array = \explode($http_post_boundary . "\r\n", $http_body);
+        if ($boundary_data_array[0] === '' || $boundary_data_array[0] === "\r\n") {
+            unset($boundary_data_array[0]);
+        }
+        $key   = -1;
+        $files = array();
+        $post_str = '';
+        foreach ($boundary_data_array as $boundary_data_buffer) {
+            list($boundary_header_buffer, $boundary_value) = \explode("\r\n\r\n", $boundary_data_buffer, 2);
+            // Remove \r\n from the end of buffer.
+            $boundary_value = \substr($boundary_value, 0, -2);
+            $key++;
+            foreach (\explode("\r\n", $boundary_header_buffer) as $item) {
+                list($header_key, $header_value) = \explode(": ", $item);
+                $header_key = \strtolower($header_key);
+                switch ($header_key) {
+                    case "content-disposition":
+                        // Is file data.
+                        if (\preg_match('/name="(.*?)"; filename="(.*?)"/i', $header_value, $match)) {
+                            $error          = 0;
+                            $tmp_file       = '';
+                            $size           = \strlen($boundary_value);
+                            $tmp_upload_dir = HTTP::uploadTmpDir();
+                            if (!$tmp_upload_dir) {
+                                $error = UPLOAD_ERR_NO_TMP_DIR;
+                            } else if ($boundary_value === '') {
+                                $error = UPLOAD_ERR_NO_FILE;
+                            } else {
+                                $tmp_file = \tempnam($tmp_upload_dir, 'workerman.upload.');
+                                if ($tmp_file === false || false == \file_put_contents($tmp_file, $boundary_value)) {
+                                    $error = UPLOAD_ERR_CANT_WRITE;
+                                }
+                            }
+                            if (!isset($files[$key])) {
+                                $files[$key] = array();
+                            }
+                            // Parse upload files.
+                            $files[$key] += array(
+                                'key'      => $match[1],
+                                'name'     => $match[2],
+                                'tmp_name' => $tmp_file,
+                                'size'     => $size,
+                                'error'    => $error,
+                                'type'     => null,
+                            );
+                            break;
+                        } // Is post field.
+                        else {
+                            // Parse $_POST.
+                            if (\preg_match('/name="(.*?)"$/', $header_value, $match)) {
+                                $key = $match[1];
+                                $post_str .= \urlencode($key)."=".\urlencode($boundary_value).'&';
+                            }
+                        }
+                        break;
+                    case "content-type":
+                        // add file_type
+                        if (!isset($files[$key])) {
+                            $files[$key] = array();
+                        }
+                        $files[$key]['type'] = \trim($header_value);
+                        break;
+                }
+            }
+        }
+        foreach ($files as $file) {
+            $key = $file['key'];
+            unset($file['key']);
+            $str = \urlencode($key)."=1";
+            $result = [];
+            \parse_str($str, $result);
+            \array_walk_recursive($result, function(&$value) use ($file) {
+                $value = $file;
+            });
+            $this->_data['files'] = \array_merge($this->_data['files'], $result);
+        }
+        if ($post_str) {
+            parse_str($post_str, $this->_data['post']);
+        }
+    }
+
+    /**
+     * Create session id.
+     *
+     * @return string
+     */
+    protected static function createSessionId()
+    {
+        return \bin2hex(\pack('d', \microtime(true)) . \pack('N', \mt_rand()));
+    }
+
+    /**
+     * Setter.
+     *
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     */
+    public function __set($name, $value)
+    {
+        $this->properties[$name] = $value;
+    }
+
+    /**
+     * Getter.
+     *
+     * @param string $name
+     * @return mixed|null
+     */
+    public function __get($name)
+    {
+        return isset($this->properties[$name]) ? $this->properties[$name] : null;
+    }
+
+    /**
+     * Isset.
+     *
+     * @param string $name
+     * @return bool
+     */
+    public function __isset($name)
+    {
+        return isset($this->properties[$name]);
+    }
+
+    /**
+     * Unset.
+     *
+     * @param string $name
+     * @return void
+     */
+    public function __unset($name)
+    {
+        unset($this->properties[$name]);
+    }
+
+    /**
+     * __toString.
+     */
+    public function __toString()
+    {
+        return $this->_buffer;
+    }
+
+    /**
+     * __destruct.
+     *
+     * @return void
+     */
+    public function __destruct()
+    {
+        if (isset($this->_data['files'])) {
+            \clearstatcache();
+            \array_walk_recursive($this->_data['files'], function($value, $key){
+                if ($key === 'tmp_name') {
+                    if (\is_file($value)) {
+                        \unlink($value);
+                    }
+                }
+            });
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Protocols/Http/Response.php b/vendor/workerman/workerman/Protocols/Http/Response.php
new file mode 100644
index 0000000..06b013a
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/Response.php
@@ -0,0 +1,458 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http;
+
+/**
+ * Class Response
+ * @package Workerman\Protocols\Http
+ */
+class Response
+{
+    /**
+     * Header data.
+     *
+     * @var array
+     */
+    protected $_header = null;
+
+    /**
+     * Http status.
+     *
+     * @var int
+     */
+    protected $_status = null;
+
+    /**
+     * Http reason.
+     *
+     * @var string
+     */
+    protected $_reason = null;
+
+    /**
+     * Http version.
+     *
+     * @var string
+     */
+    protected $_version = '1.1';
+
+    /**
+     * Http body.
+     *
+     * @var string
+     */
+    protected $_body = null;
+
+    /**
+     * Send file info
+     *
+     * @var array
+     */
+    public $file = null;
+
+    /**
+     * Mine type map.
+     * @var array
+     */
+    protected static $_mimeTypeMap = null;
+
+    /**
+     * Phrases.
+     *
+     * @var array
+     */
+    protected static $_phrases = array(
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+        102 => 'Processing',
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        207 => 'Multi-status',
+        208 => 'Already Reported',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        306 => 'Switch Proxy',
+        307 => 'Temporary Redirect',
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Time-out',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Large',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested range not satisfiable',
+        417 => 'Expectation Failed',
+        418 => 'I\'m a teapot',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        424 => 'Failed Dependency',
+        425 => 'Unordered Collection',
+        426 => 'Upgrade Required',
+        428 => 'Precondition Required',
+        429 => 'Too Many Requests',
+        431 => 'Request Header Fields Too Large',
+        451 => 'Unavailable For Legal Reasons',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Time-out',
+        505 => 'HTTP Version not supported',
+        506 => 'Variant Also Negotiates',
+        507 => 'Insufficient Storage',
+        508 => 'Loop Detected',
+        511 => 'Network Authentication Required',
+    );
+
+    /**
+     * Init.
+     *
+     * @return void
+     */
+    public static function init() {
+        static::initMimeTypeMap();
+    }
+
+    /**
+     * Response constructor.
+     *
+     * @param int $status
+     * @param array $headers
+     * @param string $body
+     */
+    public function __construct(
+        $status = 200,
+        $headers = array(),
+        $body = ''
+    ) {
+        $this->_status = $status;
+        $this->_header = $headers;
+        $this->_body = (string)$body;
+    }
+
+    /**
+     * Set header.
+     *
+     * @param string $name
+     * @param string $value
+     * @return $this
+     */
+    public function header($name, $value) {
+        $this->_header[$name] = $value;
+        return $this;
+    }
+
+    /**
+     * Set header.
+     *
+     * @param string $name
+     * @param string $value
+     * @return Response
+     */
+    public function withHeader($name, $value) {
+        return $this->header($name, $value);
+    }
+
+    /**
+     * Set headers.
+     *
+     * @param array $headers
+     * @return $this
+     */
+    public function withHeaders($headers) {
+        $this->_header = \array_merge_recursive($this->_header, $headers);
+        return $this;
+    }
+    
+    /**
+     * Remove header.
+     *
+     * @param string $name
+     * @return $this
+     */
+    public function withoutHeader($name) {
+        unset($this->_header[$name]);
+        return $this;
+    }
+
+    /**
+     * Get header.
+     *
+     * @param string $name
+     * @return null|array|string
+     */
+    public function getHeader($name) {
+        if (!isset($this->_header[$name])) {
+            return null;
+        }
+        return $this->_header[$name];
+    }
+
+    /**
+     * Get headers.
+     *
+     * @return array
+     */
+    public function getHeaders() {
+        return $this->_header;
+    }
+
+    /**
+     * Set status.
+     *
+     * @param int $code
+     * @param string|null $reason_phrase
+     * @return $this
+     */
+    public function withStatus($code, $reason_phrase = null) {
+        $this->_status = $code;
+        $this->_reason = $reason_phrase;
+        return $this;
+    }
+
+    /**
+     * Get status code.
+     *
+     * @return int
+     */
+    public function getStatusCode() {
+        return $this->_status;
+    }
+
+    /**
+     * Get reason phrase.
+     *
+     * @return string
+     */
+    public function getReasonPhrase() {
+        return $this->_reason;
+    }
+
+    /**
+     * Set protocol version.
+     *
+     * @param int $version
+     * @return $this
+     */
+    public function withProtocolVersion($version) {
+        $this->_version = $version;
+        return $this;
+    }
+
+    /**
+     * Set http body.
+     *
+     * @param string $body
+     * @return $this
+     */
+    public function withBody($body) {
+        $this->_body = $body;
+        return $this;
+    }
+
+    /**
+     * Get http raw body.
+     * 
+     * @return string
+     */
+    public function rawBody() {
+        return $this->_body;
+    }
+
+    /**
+     * Send file.
+     *
+     * @param string $file
+     * @param int $offset
+     * @param int $length
+     * @return $this
+     */
+    public function withFile($file, $offset = 0, $length = 0) {
+        if (!\is_file($file)) {
+            return $this->withStatus(404)->withBody('<h3>404 Not Found</h3>');
+        }
+        $this->file = array('file' => $file, 'offset' => $offset, 'length' => $length);
+        return $this;
+    }
+
+    /**
+     * Set cookie.
+     *
+     * @param $name
+     * @param string $value
+     * @param int $max_age
+     * @param string $path
+     * @param string $domain
+     * @param bool $secure
+     * @param bool $http_only
+     * @param bool $same_site
+     * @return $this
+     */
+    public function cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false, $same_site  = false)
+    {
+        $this->_header['Set-Cookie'][] = $name . '=' . \rawurlencode($value)
+            . (empty($domain) ? '' : '; Domain=' . $domain)
+            . (empty($max_age) ? '' : '; Max-Age=' . $max_age)
+            . (empty($path) ? '' : '; Path=' . $path)
+            . (!$secure ? '' : '; Secure')
+            . (!$http_only ? '' : '; HttpOnly')
+            . (empty($same_site ) ? '' : '; SameSite=' . $same_site);
+        return $this;
+    }
+
+    /**
+     * Create header for file.
+     *
+     * @param array $file_info
+     * @return string
+     */
+    protected function createHeadForFile($file_info)
+    {
+        $file = $file_info['file'];
+        $reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status];
+        $head = "HTTP/{$this->_version} {$this->_status} $reason\r\n";
+        $headers = $this->_header;
+        if (!isset($headers['Server'])) {
+            $head .= "Server: workerman\r\n";
+        }
+        foreach ($headers as $name => $value) {
+            if (\is_array($value)) {
+                foreach ($value as $item) {
+                    $head .= "$name: $item\r\n";
+                }
+                continue;
+            }
+            $head .= "$name: $value\r\n";
+        }
+
+        if (!isset($headers['Connection'])) {
+            $head .= "Connection: keep-alive\r\n";
+        }
+
+        $file_info = \pathinfo($file);
+        $extension = isset($file_info['extension']) ? $file_info['extension'] : '';
+        $base_name = isset($file_info['basename']) ? $file_info['basename'] : 'unknown';
+        if (!isset($headers['Content-Type'])) {
+            if (isset(self::$_mimeTypeMap[$extension])) {
+                $head .= "Content-Type: " . self::$_mimeTypeMap[$extension] . "\r\n";
+            } else {
+                $head .= "Content-Type: application/octet-stream\r\n";
+            }
+        }
+
+        if (!isset($headers['Content-Disposition']) && !isset(self::$_mimeTypeMap[$extension])) {
+            $head .= "Content-Disposition: attachment; filename=\"$base_name\"\r\n";
+        }
+
+        if (!isset($headers['Last-Modified'])) {
+            if ($mtime = \filemtime($file)) {
+                $head .= 'Last-Modified: '. \gmdate('D, d M Y H:i:s', $mtime) . ' GMT' . "\r\n";
+            }
+        }
+
+        return "{$head}\r\n";
+    }
+
+    /**
+     * __toString.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        if (isset($this->file)) {
+            return $this->createHeadForFile($this->file);
+        }
+
+        $reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status];
+        $body_len = \strlen($this->_body);
+        if (empty($this->_header)) {
+            return "HTTP/{$this->_version} {$this->_status} $reason\r\nServer: workerman\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\nConnection: keep-alive\r\n\r\n{$this->_body}";
+        }
+
+        $head = "HTTP/{$this->_version} {$this->_status} $reason\r\n";
+        $headers = $this->_header;
+        if (!isset($headers['Server'])) {
+            $head .= "Server: workerman\r\n";
+        }
+        foreach ($headers as $name => $value) {
+            if (\is_array($value)) {
+                foreach ($value as $item) {
+                    $head .= "$name: $item\r\n";
+                }
+                continue;
+            }
+            $head .= "$name: $value\r\n";
+        }
+
+        if (!isset($headers['Connection'])) {
+            $head .= "Connection: keep-alive\r\n";
+        }
+
+        if (!isset($headers['Content-Type'])) {
+            $head .= "Content-Type: text/html;charset=utf-8\r\n";
+        } else if ($headers['Content-Type'] === 'text/event-stream') {
+            return $head . $this->_body;
+        }
+
+        if (!isset($headers['Transfer-Encoding'])) {
+            $head .= "Content-Length: $body_len\r\n\r\n";
+        } else {
+            return "$head\r\n".dechex($body_len)."\r\n{$this->_body}\r\n";
+        }
+
+        // The whole http package
+        return $head . $this->_body;
+    }
+
+    /**
+     * Init mime map.
+     *
+     * @return void
+     */
+    public static function initMimeTypeMap()
+    {
+        $mime_file = __DIR__ . '/mime.types';
+        $items = \file($mime_file, \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES);
+        foreach ($items as $content) {
+            if (\preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) {
+                $mime_type       = $match[1];
+                $extension_var   = $match[2];
+                $extension_array = \explode(' ', \substr($extension_var, 0, -1));
+                foreach ($extension_array as $file_extension) {
+                    static::$_mimeTypeMap[$file_extension] = $mime_type;
+                }
+            }
+        }
+    }
+}
+Response::init();
diff --git a/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php b/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php
new file mode 100644
index 0000000..7aeafc7
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/ServerSentEvents.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http;
+
+/**
+ * Class ServerSentEvents
+ * @package Workerman\Protocols\Http
+ */
+class ServerSentEvents
+{
+    /**
+     * Data.
+     * @var array
+     */
+    protected $_data = null;
+
+    /**
+     * ServerSentEvents constructor.
+     * $data for example ['event'=>'ping', 'data' => 'some thing', 'id' => 1000, 'retry' => 5000]
+     * @param array $data
+     */
+    public function __construct(array $data)
+    {
+        $this->_data = $data;
+    }
+
+    /**
+     * __toString.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        $buffer = '';
+        $data = $this->_data;
+        if (isset($data[''])) {
+            $buffer = ": {$data['']}\n";
+        }
+        if (isset($data['event'])) {
+            $buffer .= "event: {$data['event']}\n";
+        }
+        if (isset($data['data'])) {
+            $buffer .= 'data: ' . \str_replace("\n", "\ndata: ", $data['data']) . "\n\n";
+        }
+        if (isset($data['id'])) {
+            $buffer .= "id: {$data['id']}\n";
+        }
+        if (isset($data['retry'])) {
+            $buffer .= "retry: {$data['retry']}\n";
+        }
+        return $buffer;
+    }
+}
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Protocols/Http/Session.php b/vendor/workerman/workerman/Protocols/Http/Session.php
new file mode 100644
index 0000000..e1782e1
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/Session.php
@@ -0,0 +1,370 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http;
+
+use Workerman\Protocols\Http\Session\SessionHandlerInterface;
+
+/**
+ * Class Session
+ * @package Workerman\Protocols\Http
+ */
+class Session
+{
+    /**
+     * Session andler class which implements SessionHandlerInterface.
+     *
+     * @var string
+     */
+    protected static $_handlerClass = 'Workerman\Protocols\Http\Session\FileSessionHandler';
+
+    /**
+     * Parameters of __constructor for session handler class.
+     *
+     * @var null
+     */
+    protected static $_handlerConfig = null;
+
+    /**
+     * Session.gc_probability
+     *
+     * @var int
+     */
+    protected static $_sessionGcProbability = 1;
+
+    /**
+     * Session.gc_divisor
+     *
+     * @var int
+     */
+    protected static $_sessionGcDivisor = 1000;
+
+    /**
+     * Session.gc_maxlifetime
+     *
+     * @var int
+     */
+    protected static $_sessionGcMaxLifeTime = 1440;
+
+    /**
+     * Session handler instance.
+     *
+     * @var SessionHandlerInterface
+     */
+    protected static $_handler = null;
+
+    /**
+     * Session data.
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Session changed and need to save.
+     *
+     * @var bool
+     */
+    protected $_needSave = false;
+
+    /**
+     * Session id.
+     *
+     * @var null
+     */
+    protected $_sessionId = null;
+
+    /**
+     * Session constructor.
+     *
+     * @param string $session_id
+     */
+    public function __construct($session_id)
+    {
+        static::checkSessionId($session_id);
+        if (static::$_handler === null) {
+            static::initHandler();
+        }
+        $this->_sessionId = $session_id;
+        if ($data = static::$_handler->read($session_id)) {
+            $this->_data = \unserialize($data);
+        }
+    }
+
+    /**
+     * Get session id.
+     *
+     * @return string
+     */
+    public function getId()
+    {
+        return $this->_sessionId;
+    }
+
+    /**
+     * Get session.
+     *
+     * @param string $name
+     * @param mixed|null $default
+     * @return mixed|null
+     */
+    public function get($name, $default = null)
+    {
+        return isset($this->_data[$name]) ? $this->_data[$name] : $default;
+    }
+
+    /**
+     * Store data in the session.
+     *
+     * @param string $name
+     * @param mixed $value
+     */
+    public function set($name, $value)
+    {
+        $this->_data[$name] = $value;
+        $this->_needSave = true;
+    }
+
+    /**
+     * Delete an item from the session.
+     *
+     * @param string $name
+     */
+    public function delete($name)
+    {
+        unset($this->_data[$name]);
+        $this->_needSave = true;
+    }
+
+    /**
+     * Retrieve and delete an item from the session.
+     *
+     * @param string $name
+     * @param mixed|null $default
+     * @return mixed|null
+     */
+    public function pull($name, $default = null)
+    {
+        $value = $this->get($name, $default);
+        $this->delete($name);
+        return $value;
+    }
+
+    /**
+     * Store data in the session.
+     *
+     * @param string $key
+     * @param mixed|null $value
+     */
+    public function put($key, $value = null)
+    {
+        if (!\is_array($key)) {
+            $this->set($key, $value);
+            return;
+        }
+
+        foreach ($key as $k => $v) {
+            $this->_data[$k] = $v;
+        }
+        $this->_needSave = true;
+    }
+
+    /**
+     * Remove a piece of data from the session.
+     *
+     * @param string $name
+     */
+    public function forget($name)
+    {
+        if (\is_scalar($name)) {
+            $this->delete($name);
+            return;
+        }
+        if (\is_array($name)) {
+            foreach ($name as $key) {
+                unset($this->_data[$key]);
+            }
+        }
+        $this->_needSave = true;
+    }
+
+    /**
+     * Retrieve all the data in the session.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Remove all data from the session.
+     *
+     * @return void
+     */
+    public function flush()
+    {
+        $this->_needSave = true;
+        $this->_data = array();
+    }
+
+    /**
+     * Determining If An Item Exists In The Session.
+     *
+     * @param string $name
+     * @return bool
+     */
+    public function has($name)
+    {
+        return isset($this->_data[$name]);
+    }
+
+    /**
+     * To determine if an item is present in the session, even if its value is null.
+     *
+     * @param string $name
+     * @return bool
+     */
+    public function exists($name)
+    {
+        return \array_key_exists($name, $this->_data);
+    }
+
+    /**
+     * Save session to store.
+     *
+     * @return void
+     */
+    public function save()
+    {
+        if ($this->_needSave) {
+            if (empty($this->_data)) {
+                static::$_handler->destroy($this->_sessionId);
+            } else {
+                static::$_handler->write($this->_sessionId, \serialize($this->_data));
+            }
+        }
+        $this->_needSave = false;
+    }
+
+    /**
+     * Refresh session expire time.
+     * 
+     * @return bool
+     */
+    public function refresh()
+    {
+        static::$_handler->updateTimestamp($this->getId());
+    }
+
+    /**
+     * Init.
+     *
+     * @return void
+     */
+    public static function init()
+    {
+        if ($gc_probability = \ini_get('session.gc_probability')) {
+            self::$_sessionGcProbability = (int)$gc_probability;
+        }
+
+        if ($gc_divisor = \ini_get('session.gc_divisor')) {
+            self::$_sessionGcDivisor = (int)$gc_divisor;
+        }
+
+        if ($gc_max_life_time = \ini_get('session.gc_maxlifetime')) {
+            self::$_sessionGcMaxLifeTime = (int)$gc_max_life_time;
+        }
+    }
+
+    /**
+     * Set session handler class.
+     *
+     * @param mixed|null $class_name
+     * @param mixed|null $config
+     * @return string
+     */
+    public static function handlerClass($class_name = null, $config = null)
+    {
+        if ($class_name) {
+            static::$_handlerClass = $class_name;
+        }
+        if ($config) {
+            static::$_handlerConfig = $config;
+        }
+        return static::$_handlerClass;
+    }
+
+    /**
+     * Init handler.
+     *
+     * @return void
+     */
+    protected static function initHandler()
+    {
+        if (static::$_handlerConfig === null) {
+            static::$_handler = new static::$_handlerClass();
+        } else {
+            static::$_handler = new static::$_handlerClass(static::$_handlerConfig);
+        }
+    }
+
+    /**
+     * Try GC sessions.
+     *
+     * @return void
+     */
+    public function tryGcSessions()
+    {
+        if (\rand(1, static::$_sessionGcDivisor) > static::$_sessionGcProbability) {
+            return;
+        }
+        static::$_handler->gc(static::$_sessionGcMaxLifeTime);
+    }
+
+    /**
+     * __destruct.
+     *
+     * @return void
+     */
+    public function __destruct()
+    {
+        $this->save();
+        $this->tryGcSessions();
+    }
+
+    /**
+     * Check session id.
+     *
+     * @param string $session_id
+     */
+    protected static function checkSessionId($session_id)
+    {
+        if (!\preg_match('/^[a-zA-Z0-9]+$/', $session_id)) {
+            throw new SessionException("session_id $session_id is invalid");
+        }
+    }
+}
+
+/**
+ * Class SessionException
+ * @package Workerman\Protocols\Http
+ */
+class SessionException extends \RuntimeException
+{
+
+}
+
+// Init session.
+Session::init();
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php b/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php
new file mode 100644
index 0000000..5c862a5
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/Session/FileSessionHandler.php
@@ -0,0 +1,177 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http\Session;
+
+/**
+ * Class FileSessionHandler
+ * @package Workerman\Protocols\Http\Session
+ */
+class FileSessionHandler implements SessionHandlerInterface
+{
+    /**
+     * Session save path.
+     *
+     * @var string
+     */
+    protected static $_sessionSavePath = null;
+
+    /**
+     * Session file prefix.
+     *
+     * @var string
+     */
+    protected static $_sessionFilePrefix = 'session_';
+
+    /**
+     * Init.
+     */
+    public static function init() {
+        $save_path = @\session_save_path();
+        if (!$save_path || \strpos($save_path, 'tcp://') === 0) {
+            $save_path = \sys_get_temp_dir();
+        }
+        static::sessionSavePath($save_path);
+    }
+
+    /**
+     * FileSessionHandler constructor.
+     * @param array $config
+     */
+    public function __construct($config = array()) {
+        if (isset($config['save_path'])) {
+            static::sessionSavePath($config['save_path']);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($save_path, $name)
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($session_id)
+    {
+        $session_file = static::sessionFile($session_id);
+        \clearstatcache();
+        if (\is_file($session_file)) {
+            $data = \file_get_contents($session_file);
+            return $data ? $data : '';
+        }
+        return '';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function write($session_id, $session_data)
+    {
+        $temp_file = static::$_sessionSavePath.uniqid(mt_rand(), true);
+        if (!\file_put_contents($temp_file, $session_data)) {
+            return false;
+        }
+        return \rename($temp_file, static::sessionFile($session_id));
+    }
+
+    /**
+     * Update sesstion modify time.
+     * 
+     * @see https://www.php.net/manual/en/class.sessionupdatetimestamphandlerinterface.php
+     * @see https://www.php.net/manual/zh/function.touch.php
+     * 
+     * @param string $id Session id.
+     * @param string $data Session Data.
+     * 
+     * @return bool
+     */
+    public function updateTimestamp($id, $data = "")
+    {
+        $session_file = static::sessionFile($id);
+        if (!file_exists($session_file)) {
+            return false;
+        }
+        // set file modify time to current time
+        $set_modify_time = \touch($session_file);
+        // clear file stat cache
+        \clearstatcache();
+        return $set_modify_time;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($session_id)
+    {
+        $session_file = static::sessionFile($session_id);
+        if (\is_file($session_file)) {
+            \unlink($session_file);
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime) {
+        $time_now = \time();
+        foreach (\glob(static::$_sessionSavePath . static::$_sessionFilePrefix . '*') as $file) {
+            if(\is_file($file) && $time_now - \filemtime($file) > $maxlifetime) {
+                \unlink($file);
+            }
+        }
+    }
+
+    /**
+     * Get session file path.
+     *
+     * @param string $session_id
+     * @return string
+     */
+    protected static function sessionFile($session_id) {
+        return static::$_sessionSavePath.static::$_sessionFilePrefix.$session_id;
+    }
+
+    /**
+     * Get or set session file path.
+     *
+     * @param string $path
+     * @return string
+     */
+    public static function sessionSavePath($path) {
+        if ($path) {
+            if ($path[\strlen($path)-1] !== DIRECTORY_SEPARATOR) {
+                $path .= DIRECTORY_SEPARATOR;
+            }
+            static::$_sessionSavePath = $path;
+            if (!\is_dir($path)) {
+                \mkdir($path, 0777, true);
+            }
+        }
+        return $path;
+    }
+}
+
+FileSessionHandler::init();
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php b/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php
new file mode 100644
index 0000000..8cfe2cb
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/Session/RedisSessionHandler.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http\Session;
+
+/**
+ * Class RedisSessionHandler
+ * @package Workerman\Protocols\Http\Session
+ */
+class RedisSessionHandler implements SessionHandlerInterface
+{
+
+    /**
+     * @var \Redis
+     */
+    protected $_redis;
+
+    /**
+     * @var int
+     */
+    protected $_maxLifeTime;
+
+    /**
+     * RedisSessionHandler constructor.
+     * @param array $config = [
+     *  'host'     => '127.0.0.1',
+     *  'port'     => 6379,
+     *  'timeout'  => 2,
+     *  'auth'     => '******',
+     *  'database' => 2,
+     *  'prefix'   => 'redis_session_',
+     * ]
+     */
+    public function __construct($config)
+    {
+        if (false === extension_loaded('redis')) {
+            throw new \RuntimeException('Please install redis extension.');
+        }
+        $this->_maxLifeTime = (int)ini_get('session.gc_maxlifetime');
+
+        if (!isset($config['timeout'])) {
+            $config['timeout'] = 2;
+        }
+
+        $this->_redis = new \Redis();
+        if (false === $this->_redis->connect($config['host'], $config['port'], $config['timeout'])) {
+            throw new \RuntimeException("Redis connect {$config['host']}:{$config['port']} fail.");
+        }
+        if (!empty($config['auth'])) {
+            $this->_redis->auth($config['auth']);
+        }
+        if (!empty($config['database'])) {
+            $this->_redis->select($config['database']);
+        }
+        if (empty($config['prefix'])) {
+            $config['prefix'] = 'redis_session_';
+        }
+        $this->_redis->setOption(\Redis::OPT_PREFIX, $config['prefix']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function open($save_path, $name)
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($session_id)
+    {
+        return $this->_redis->get($session_id);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function write($session_id, $session_data)
+    {
+        return true === $this->_redis->setex($session_id, $this->_maxLifeTime, $session_data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updateTimestamp($id, $data = "")
+    {
+        return true === $this->_redis->expire($id, $this->_maxLifeTime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function destroy($session_id)
+    {
+        $this->_redis->del($session_id);
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function gc($maxlifetime)
+    {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php b/vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php
new file mode 100644
index 0000000..23a47f2
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/Session/SessionHandlerInterface.php
@@ -0,0 +1,114 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols\Http\Session;
+
+interface SessionHandlerInterface
+{
+    /**
+     * Close the session
+     * @link http://php.net/manual/en/sessionhandlerinterface.close.php
+     * @return bool <p>
+     * The return value (usually TRUE on success, FALSE on failure).
+     * Note this value is returned internally to PHP for processing.
+     * </p>
+     * @since 5.4.0
+     */
+    public function close();
+
+    /**
+     * Destroy a session
+     * @link http://php.net/manual/en/sessionhandlerinterface.destroy.php
+     * @param string $session_id The session ID being destroyed.
+     * @return bool <p>
+     * The return value (usually TRUE on success, FALSE on failure).
+     * Note this value is returned internally to PHP for processing.
+     * </p>
+     * @since 5.4.0
+     */
+    public function destroy($session_id);
+
+    /**
+     * Cleanup old sessions
+     * @link http://php.net/manual/en/sessionhandlerinterface.gc.php
+     * @param int $maxlifetime <p>
+     * Sessions that have not updated for
+     * the last maxlifetime seconds will be removed.
+     * </p>
+     * @return bool <p>
+     * The return value (usually TRUE on success, FALSE on failure).
+     * Note this value is returned internally to PHP for processing.
+     * </p>
+     * @since 5.4.0
+     */
+    public function gc($maxlifetime);
+
+    /**
+     * Initialize session
+     * @link http://php.net/manual/en/sessionhandlerinterface.open.php
+     * @param string $save_path The path where to store/retrieve the session.
+     * @param string $name The session name.
+     * @return bool <p>
+     * The return value (usually TRUE on success, FALSE on failure).
+     * Note this value is returned internally to PHP for processing.
+     * </p>
+     * @since 5.4.0
+     */
+    public function open($save_path, $name);
+
+
+    /**
+     * Read session data
+     * @link http://php.net/manual/en/sessionhandlerinterface.read.php
+     * @param string $session_id The session id to read data for.
+     * @return string <p>
+     * Returns an encoded string of the read data.
+     * If nothing was read, it must return an empty string.
+     * Note this value is returned internally to PHP for processing.
+     * </p>
+     * @since 5.4.0
+     */
+    public function read($session_id);
+
+    /**
+     * Write session data
+     * @link http://php.net/manual/en/sessionhandlerinterface.write.php
+     * @param string $session_id The session id.
+     * @param string $session_data <p>
+     * The encoded session data. This data is the
+     * result of the PHP internally encoding
+     * the $_SESSION superglobal to a serialized
+     * string and passing it as this parameter.
+     * Please note sessions use an alternative serialization method.
+     * </p>
+     * @return bool <p>
+     * The return value (usually TRUE on success, FALSE on failure).
+     * Note this value is returned internally to PHP for processing.
+     * </p>
+     * @since 5.4.0
+     */
+    public function write($session_id, $session_data);
+
+    /**
+     * Update sesstion modify time.
+     * 
+     * @see https://www.php.net/manual/en/class.sessionupdatetimestamphandlerinterface.php
+     * 
+     * @param string $id Session id.
+     * @param string $data Session Data.
+     * 
+     * @return bool
+     */
+    public function updateTimestamp($id, $data = "");
+
+}
diff --git a/vendor/workerman/workerman/Protocols/Http/mime.types b/vendor/workerman/workerman/Protocols/Http/mime.types
new file mode 100644
index 0000000..e6ccf0a
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Http/mime.types
@@ -0,0 +1,90 @@
+
+types {
+    text/html                             html htm shtml;
+    text/css                              css;
+    text/xml                              xml;
+    image/gif                             gif;
+    image/jpeg                            jpeg jpg;
+    application/javascript                js;
+    application/atom+xml                  atom;
+    application/rss+xml                   rss;
+
+    text/mathml                           mml;
+    text/plain                            txt;
+    text/vnd.sun.j2me.app-descriptor      jad;
+    text/vnd.wap.wml                      wml;
+    text/x-component                      htc;
+
+    image/png                             png;
+    image/tiff                            tif tiff;
+    image/vnd.wap.wbmp                    wbmp;
+    image/x-icon                          ico;
+    image/x-jng                           jng;
+    image/x-ms-bmp                        bmp;
+    image/svg+xml                         svg svgz;
+    image/webp                            webp;
+
+    application/font-woff                 woff;
+    application/java-archive              jar war ear;
+    application/json                      json;
+    application/mac-binhex40              hqx;
+    application/msword                    doc;
+    application/pdf                       pdf;
+    application/postscript                ps eps ai;
+    application/rtf                       rtf;
+    application/vnd.apple.mpegurl         m3u8;
+    application/vnd.ms-excel              xls;
+    application/vnd.ms-fontobject         eot;
+    application/vnd.ms-powerpoint         ppt;
+    application/vnd.wap.wmlc              wmlc;
+    application/vnd.google-earth.kml+xml  kml;
+    application/vnd.google-earth.kmz      kmz;
+    application/x-7z-compressed           7z;
+    application/x-cocoa                   cco;
+    application/x-java-archive-diff       jardiff;
+    application/x-java-jnlp-file          jnlp;
+    application/x-makeself                run;
+    application/x-perl                    pl pm;
+    application/x-pilot                   prc pdb;
+    application/x-rar-compressed          rar;
+    application/x-redhat-package-manager  rpm;
+    application/x-sea                     sea;
+    application/x-shockwave-flash         swf;
+    application/x-stuffit                 sit;
+    application/x-tcl                     tcl tk;
+    application/x-x509-ca-cert            der pem crt;
+    application/x-xpinstall               xpi;
+    application/xhtml+xml                 xhtml;
+    application/xspf+xml                  xspf;
+    application/zip                       zip;
+
+    application/octet-stream              bin exe dll;
+    application/octet-stream              deb;
+    application/octet-stream              dmg;
+    application/octet-stream              iso img;
+    application/octet-stream              msi msp msm;
+
+    application/vnd.openxmlformats-officedocument.wordprocessingml.document    docx;
+    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet          xlsx;
+    application/vnd.openxmlformats-officedocument.presentationml.presentation  pptx;
+
+    audio/midi                            mid midi kar;
+    audio/mpeg                            mp3;
+    audio/ogg                             ogg;
+    audio/x-m4a                           m4a;
+    audio/x-realaudio                     ra;
+
+    video/3gpp                            3gpp 3gp;
+    video/mp2t                            ts;
+    video/mp4                             mp4;
+    video/mpeg                            mpeg mpg;
+    video/quicktime                       mov;
+    video/webm                            webm;
+    video/x-flv                           flv;
+    video/x-m4v                           m4v;
+    video/x-mng                           mng;
+    video/x-ms-asf                        asx asf;
+    video/x-ms-wmv                        wmv;
+    video/x-msvideo                       avi;
+    font/ttf                              ttf;
+}
diff --git a/vendor/workerman/workerman/Protocols/ProtocolInterface.php b/vendor/workerman/workerman/Protocols/ProtocolInterface.php
new file mode 100644
index 0000000..4fea87d
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/ProtocolInterface.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\ConnectionInterface;
+
+/**
+ * Protocol interface
+ */
+interface ProtocolInterface
+{
+    /**
+     * Check the integrity of the package.
+     * Please return the length of package.
+     * If length is unknow please return 0 that mean wating more data.
+     * If the package has something wrong please return false the connection will be closed.
+     *
+     * @param string              $recv_buffer
+     * @param ConnectionInterface $connection
+     * @return int|false
+     */
+    public static function input($recv_buffer, ConnectionInterface $connection);
+
+    /**
+     * Decode package and emit onMessage($message) callback, $message is the result that decode returned.
+     *
+     * @param string              $recv_buffer
+     * @param ConnectionInterface $connection
+     * @return mixed
+     */
+    public static function decode($recv_buffer, ConnectionInterface $connection);
+
+    /**
+     * Encode package brefore sending to client.
+     * 
+     * @param mixed               $data
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function encode($data, ConnectionInterface $connection);
+}
diff --git a/vendor/workerman/workerman/Protocols/Text.php b/vendor/workerman/workerman/Protocols/Text.php
new file mode 100644
index 0000000..407ea2d
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Text.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\ConnectionInterface;
+
+/**
+ * Text Protocol.
+ */
+class Text
+{
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string        $buffer
+     * @param ConnectionInterface $connection
+     * @return int
+     */
+    public static function input($buffer, ConnectionInterface $connection)
+    {
+        // Judge whether the package length exceeds the limit.
+        if (isset($connection->maxPackageSize) && \strlen($buffer) >= $connection->maxPackageSize) {
+            $connection->close();
+            return 0;
+        }
+        //  Find the position of  "\n".
+        $pos = \strpos($buffer, "\n");
+        // No "\n", packet length is unknown, continue to wait for the data so return 0.
+        if ($pos === false) {
+            return 0;
+        }
+        // Return the current package length.
+        return $pos + 1;
+    }
+
+    /**
+     * Encode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function encode($buffer)
+    {
+        // Add "\n"
+        return $buffer . "\n";
+    }
+
+    /**
+     * Decode.
+     *
+     * @param string $buffer
+     * @return string
+     */
+    public static function decode($buffer)
+    {
+        // Remove "\n"
+        return \rtrim($buffer, "\r\n");
+    }
+}
\ No newline at end of file
diff --git a/vendor/workerman/workerman/Protocols/Websocket.php b/vendor/workerman/workerman/Protocols/Websocket.php
new file mode 100644
index 0000000..2cc2fb7
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Websocket.php
@@ -0,0 +1,492 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Connection\ConnectionInterface;
+use Workerman\Connection\TcpConnection;
+use Workerman\Worker;
+
+/**
+ * WebSocket protocol.
+ */
+class Websocket implements \Workerman\Protocols\ProtocolInterface
+{
+    /**
+     * Websocket blob type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_BLOB = "\x81";
+
+    /**
+     * Websocket arraybuffer type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_ARRAYBUFFER = "\x82";
+
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return int
+     */
+    public static function input($buffer, ConnectionInterface $connection)
+    {
+        // Receive length.
+        $recv_len = \strlen($buffer);
+        // We need more data.
+        if ($recv_len < 6) {
+            return 0;
+        }
+
+        // Has not yet completed the handshake.
+        if (empty($connection->websocketHandshake)) {
+            return static::dealHandshake($buffer, $connection);
+        }
+
+        // Buffer websocket frame data.
+        if ($connection->websocketCurrentFrameLength) {
+            // We need more frame data.
+            if ($connection->websocketCurrentFrameLength > $recv_len) {
+                // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
+                return 0;
+            }
+        } else {
+            $firstbyte    = \ord($buffer[0]);
+            $secondbyte   = \ord($buffer[1]);
+            $data_len     = $secondbyte & 127;
+            $is_fin_frame = $firstbyte >> 7;
+            $masked       = $secondbyte >> 7;
+
+            if (!$masked) {
+                Worker::safeEcho("frame not masked so close the connection\n");
+                $connection->close();
+                return 0;
+            }
+
+            $opcode       = $firstbyte & 0xf;
+            switch ($opcode) {
+                case 0x0:
+                    break;
+                // Blob type.
+                case 0x1:
+                    break;
+                // Arraybuffer type.
+                case 0x2:
+                    break;
+                // Close package.
+                case 0x8:
+                    // Try to emit onWebSocketClose callback.
+                    if (isset($connection->onWebSocketClose) || isset($connection->worker->onWebSocketClose)) {
+                        try {
+                            \call_user_func(isset($connection->onWebSocketClose)?$connection->onWebSocketClose:$connection->worker->onWebSocketClose, $connection);
+                        } catch (\Exception $e) {
+                            Worker::stopAll(250, $e);
+                        } catch (\Error $e) {
+                            Worker::stopAll(250, $e);
+                        }
+                    } // Close connection.
+                    else {
+                        $connection->close("\x88\x02\x03\xe8", true);
+                    }
+                    return 0;
+                // Ping package.
+                case 0x9:
+                    break;
+                // Pong package.
+                case 0xa:
+                    break;
+                // Wrong opcode. 
+                default :
+                    Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . bin2hex($buffer) . "\n");
+                    $connection->close();
+                    return 0;
+            }
+
+            // Calculate packet length.
+            $head_len = 6;
+            if ($data_len === 126) {
+                $head_len = 8;
+                if ($head_len > $recv_len) {
+                    return 0;
+                }
+                $pack     = \unpack('nn/ntotal_len', $buffer);
+                $data_len = $pack['total_len'];
+            } else {
+                if ($data_len === 127) {
+                    $head_len = 14;
+                    if ($head_len > $recv_len) {
+                        return 0;
+                    }
+                    $arr      = \unpack('n/N2c', $buffer);
+                    $data_len = $arr['c1']*4294967296 + $arr['c2'];
+                }
+            }
+            $current_frame_length = $head_len + $data_len;
+
+            $total_package_size = \strlen($connection->websocketDataBuffer) + $current_frame_length;
+            if ($total_package_size > $connection->maxPackageSize) {
+                Worker::safeEcho("error package. package_length=$total_package_size\n");
+                $connection->close();
+                return 0;
+            }
+
+            if ($is_fin_frame) {
+                if ($opcode === 0x9) {
+                    if ($recv_len >= $current_frame_length) {
+                        $ping_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        if (isset($connection->onWebSocketPing) || isset($connection->worker->onWebSocketPing)) {
+                            try {
+                                \call_user_func(isset($connection->onWebSocketPing)?$connection->onWebSocketPing:$connection->worker->onWebSocketPing, $connection, $ping_data);
+                            } catch (\Exception $e) {
+                                Worker::stopAll(250, $e);
+                            } catch (\Error $e) {
+                                Worker::stopAll(250, $e);
+                            }
+                        } else {
+                            $connection->send($ping_data);
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(\substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+                } else if ($opcode === 0xa) {
+                    if ($recv_len >= $current_frame_length) {
+                        $pong_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        // Try to emit onWebSocketPong callback.
+                        if (isset($connection->onWebSocketPong) || isset($connection->worker->onWebSocketPong)) {
+                            try {
+                                \call_user_func(isset($connection->onWebSocketPong)?$connection->onWebSocketPong:$connection->worker->onWebSocketPong, $connection, $pong_data);
+                            } catch (\Exception $e) {
+                                Worker::stopAll(250, $e);
+                            } catch (\Error $e) {
+                                Worker::stopAll(250, $e);
+                            }
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(\substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+                }
+                return $current_frame_length;
+            } else {
+                $connection->websocketCurrentFrameLength = $current_frame_length;
+            }
+        }
+
+        // Received just a frame length data.
+        if ($connection->websocketCurrentFrameLength === $recv_len) {
+            static::decode($buffer, $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $connection->websocketCurrentFrameLength = 0;
+            return 0;
+        } // The length of the received data is greater than the length of a frame.
+        elseif ($connection->websocketCurrentFrameLength < $recv_len) {
+            static::decode(\substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $current_frame_length                    = $connection->websocketCurrentFrameLength;
+            $connection->websocketCurrentFrameLength = 0;
+            // Continue to read next frame.
+            return static::input(\substr($buffer, $current_frame_length), $connection);
+        } // The length of the received data is less than the length of a frame.
+        else {
+            return 0;
+        }
+    }
+
+    /**
+     * Websocket encode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function encode($buffer, ConnectionInterface $connection)
+    {
+        if (!is_scalar($buffer)) {
+            throw new \Exception("You can't send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
+        }
+        $len = \strlen($buffer);
+        if (empty($connection->websocketType)) {
+            $connection->websocketType = static::BINARY_TYPE_BLOB;
+        }
+
+        $first_byte = $connection->websocketType;
+
+        if ($len <= 125) {
+            $encode_buffer = $first_byte . \chr($len) . $buffer;
+        } else {
+            if ($len <= 65535) {
+                $encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
+            } else {
+                $encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
+            }
+        }
+
+        // Handshake not completed so temporary buffer websocket data waiting for send.
+        if (empty($connection->websocketHandshake)) {
+            if (empty($connection->tmpWebsocketData)) {
+                $connection->tmpWebsocketData = '';
+            }
+            // If buffer has already full then discard the current package.
+            if (\strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) {
+                if ($connection->onError) {
+                    try {
+                        \call_user_func($connection->onError, $connection, \WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
+                    } catch (\Exception $e) {
+                        Worker::stopAll(250, $e);
+                    } catch (\Error $e) {
+                        Worker::stopAll(250, $e);
+                    }
+                }
+                return '';
+            }
+            $connection->tmpWebsocketData .= $encode_buffer;
+            // Check buffer is full.
+            if ($connection->maxSendBufferSize <= \strlen($connection->tmpWebsocketData)) {
+                if ($connection->onBufferFull) {
+                    try {
+                        \call_user_func($connection->onBufferFull, $connection);
+                    } catch (\Exception $e) {
+                        Worker::stopAll(250, $e);
+                    } catch (\Error $e) {
+                        Worker::stopAll(250, $e);
+                    }
+                }
+            }
+
+            // Return empty string.
+            return '';
+        }
+
+        return $encode_buffer;
+    }
+
+    /**
+     * Websocket decode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function decode($buffer, ConnectionInterface $connection)
+    {
+        $len = \ord($buffer[1]) & 127;
+        if ($len === 126) {
+            $masks = \substr($buffer, 4, 4);
+            $data  = \substr($buffer, 8);
+        } else {
+            if ($len === 127) {
+                $masks = \substr($buffer, 10, 4);
+                $data  = \substr($buffer, 14);
+            } else {
+                $masks = \substr($buffer, 2, 4);
+                $data  = \substr($buffer, 6);
+            }
+        }
+        $dataLength = \strlen($data);
+        $masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
+        $decoded = $data ^ $masks;
+        if ($connection->websocketCurrentFrameLength) {
+            $connection->websocketDataBuffer .= $decoded;
+            return $connection->websocketDataBuffer;
+        } else {
+            if ($connection->websocketDataBuffer !== '') {
+                $decoded                         = $connection->websocketDataBuffer . $decoded;
+                $connection->websocketDataBuffer = '';
+            }
+            return $decoded;
+        }
+    }
+
+    /**
+     * Websocket handshake.
+     *
+     * @param string                              $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function dealHandshake($buffer, TcpConnection $connection)
+    {
+        // HTTP protocol.
+        if (0 === \strpos($buffer, 'GET')) {
+            // Find \r\n\r\n.
+            $heder_end_pos = \strpos($buffer, "\r\n\r\n");
+            if (!$heder_end_pos) {
+                return 0;
+            }
+            $header_length = $heder_end_pos + 4;
+
+            // Get Sec-WebSocket-Key.
+            $Sec_WebSocket_Key = '';
+            if (\preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
+                $Sec_WebSocket_Key = $match[1];
+            } else {
+                $connection->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/".Worker::VERSION."\r\n\r\n<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman/".Worker::VERSION."</div>",
+                    true);
+                return 0;
+            }
+            // Calculation websocket key.
+            $new_key = \base64_encode(\sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
+            // Handshake response data.
+            $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n"
+                                ."Upgrade: websocket\r\n"
+                                ."Sec-WebSocket-Version: 13\r\n"
+                                ."Connection: Upgrade\r\n"
+                                ."Sec-WebSocket-Accept: " . $new_key . "\r\n";
+
+            // Websocket data buffer.
+            $connection->websocketDataBuffer = '';
+            // Current websocket frame length.
+            $connection->websocketCurrentFrameLength = 0;
+            // Current websocket frame data.
+            $connection->websocketCurrentFrameBuffer = '';
+            // Consume handshake data.
+            $connection->consumeRecvBuffer($header_length);
+
+            // blob or arraybuffer
+            if (empty($connection->websocketType)) {
+                $connection->websocketType = static::BINARY_TYPE_BLOB;
+            }
+
+            $has_server_header = false;
+
+            if (isset($connection->headers)) {
+                if (\is_array($connection->headers))  {
+                    foreach ($connection->headers as $header) {
+                        if (\strpos($header, 'Server:') === 0) {
+                            $has_server_header = true;
+                        }
+                        $handshake_message .= "$header\r\n";
+                    }
+                } else {
+                    $handshake_message .= "$connection->headers\r\n";
+                }
+            }
+            if (!$has_server_header) {
+                $handshake_message .= "Server: workerman/".Worker::VERSION."\r\n";
+            }
+            $handshake_message .= "\r\n";
+            // Send handshake response.
+            $connection->send($handshake_message, true);
+            // Mark handshake complete..
+            $connection->websocketHandshake = true;
+
+            // Try to emit onWebSocketConnect callback.
+            $on_websocket_connect = isset($connection->onWebSocketConnect) ? $connection->onWebSocketConnect :
+                (isset($connection->worker->onWebSocketConnect) ? $connection->worker->onWebSocketConnect : false);
+            if ($on_websocket_connect) {
+                static::parseHttpHeader($buffer);
+                try {
+                    \call_user_func($on_websocket_connect, $connection, $buffer);
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+                if (!empty($_SESSION) && \class_exists('\GatewayWorker\Lib\Context')) {
+                    $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION);
+                }
+                $_GET = $_SERVER = $_SESSION = $_COOKIE = array();
+            }
+
+            // There are data waiting to be sent.
+            if (!empty($connection->tmpWebsocketData)) {
+                $connection->send($connection->tmpWebsocketData, true);
+                $connection->tmpWebsocketData = '';
+            }
+            if (\strlen($buffer) > $header_length) {
+                return static::input(\substr($buffer, $header_length), $connection);
+            }
+            return 0;
+        } // Is flash policy-file-request.
+        elseif (0 === \strpos($buffer, '<polic')) {
+            $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>' . "\0";
+            $connection->send($policy_xml, true);
+            $connection->consumeRecvBuffer(\strlen($buffer));
+            return 0;
+        }
+        // Bad websocket handshake request.
+        $connection->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/".Worker::VERSION."\r\n\r\n<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman/".Worker::VERSION."</div>",
+            true);
+        return 0;
+    }
+
+    /**
+     * Parse http header.
+     *
+     * @param string $buffer
+     * @return void
+     */
+    protected static function parseHttpHeader($buffer)
+    {
+        // Parse headers.
+        list($http_header, ) = \explode("\r\n\r\n", $buffer, 2);
+        $header_data = \explode("\r\n", $http_header);
+
+        if ($_SERVER) {
+            $_SERVER = array();
+        }
+
+        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = \explode(' ',
+            $header_data[0]);
+
+        unset($header_data[0]);
+        foreach ($header_data as $content) {
+            // \r\n\r\n
+            if (empty($content)) {
+                continue;
+            }
+            list($key, $value)       = \explode(':', $content, 2);
+            $key                     = \str_replace('-', '_', \strtoupper($key));
+            $value                   = \trim($value);
+            $_SERVER['HTTP_' . $key] = $value;
+            switch ($key) {
+                // HTTP_HOST
+                case 'HOST':
+                    $tmp                    = \explode(':', $value);
+                    $_SERVER['SERVER_NAME'] = $tmp[0];
+                    if (isset($tmp[1])) {
+                        $_SERVER['SERVER_PORT'] = $tmp[1];
+                    }
+                    break;
+                // cookie
+                case 'COOKIE':
+                    \parse_str(\str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
+                    break;
+            }
+        }
+
+        // QUERY_STRING
+        $_SERVER['QUERY_STRING'] = \parse_url($_SERVER['REQUEST_URI'], \PHP_URL_QUERY);
+        if ($_SERVER['QUERY_STRING']) {
+            // $GET
+            \parse_str($_SERVER['QUERY_STRING'], $_GET);
+        } else {
+            $_SERVER['QUERY_STRING'] = '';
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Protocols/Ws.php b/vendor/workerman/workerman/Protocols/Ws.php
new file mode 100644
index 0000000..449a419
--- /dev/null
+++ b/vendor/workerman/workerman/Protocols/Ws.php
@@ -0,0 +1,460 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman\Protocols;
+
+use Workerman\Worker;
+use Workerman\Lib\Timer;
+use Workerman\Connection\TcpConnection;
+use Workerman\Connection\ConnectionInterface;
+
+/**
+ * Websocket protocol for client.
+ */
+class Ws
+{
+    /**
+     * Websocket blob type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_BLOB = "\x81";
+
+    /**
+     * Websocket arraybuffer type.
+     *
+     * @var string
+     */
+    const BINARY_TYPE_ARRAYBUFFER = "\x82";
+
+    /**
+     * Check the integrity of the package.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return int
+     */
+    public static function input($buffer, ConnectionInterface $connection)
+    {
+        if (empty($connection->handshakeStep)) {
+            Worker::safeEcho("recv data before handshake. Buffer:" . \bin2hex($buffer) . "\n");
+            return false;
+        }
+        // Recv handshake response
+        if ($connection->handshakeStep === 1) {
+            return self::dealHandshake($buffer, $connection);
+        }
+        $recv_len = \strlen($buffer);
+        if ($recv_len < 2) {
+            return 0;
+        }
+        // Buffer websocket frame data.
+        if ($connection->websocketCurrentFrameLength) {
+            // We need more frame data.
+            if ($connection->websocketCurrentFrameLength > $recv_len) {
+                // Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
+                return 0;
+            }
+        } else {
+
+            $firstbyte    = \ord($buffer[0]);
+            $secondbyte   = \ord($buffer[1]);
+            $data_len     = $secondbyte & 127;
+            $is_fin_frame = $firstbyte >> 7;
+            $masked       = $secondbyte >> 7;
+
+            if ($masked) {
+                Worker::safeEcho("frame masked so close the connection\n");
+                $connection->close();
+                return 0;
+            }
+
+            $opcode       = $firstbyte & 0xf;
+
+            switch ($opcode) {
+                case 0x0:
+                    break;
+                // Blob type.
+                case 0x1:
+                    break;
+                // Arraybuffer type.
+                case 0x2:
+                    break;
+                // Close package.
+                case 0x8:
+                    // Try to emit onWebSocketClose callback.
+                    if (isset($connection->onWebSocketClose)) {
+                        try {
+                            \call_user_func($connection->onWebSocketClose, $connection);
+                        } catch (\Exception $e) {
+                            Worker::stopAll(250, $e);
+                        } catch (\Error $e) {
+                            Worker::stopAll(250, $e);
+                        }
+                    } // Close connection.
+                    else {
+                        $connection->close();
+                    }
+                    return 0;
+                // Ping package.
+                case 0x9:
+                    break;
+                // Pong package.
+                case 0xa:
+                    break;
+                // Wrong opcode.
+                default :
+                    Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . $buffer . "\n");
+                    $connection->close();
+                    return 0;
+            }
+            // Calculate packet length.
+            if ($data_len === 126) {
+                if (\strlen($buffer) < 4) {
+                    return 0;
+                }
+                $pack = \unpack('nn/ntotal_len', $buffer);
+                $current_frame_length = $pack['total_len'] + 4;
+            } else if ($data_len === 127) {
+                if (\strlen($buffer) < 10) {
+                    return 0;
+                }
+                $arr = \unpack('n/N2c', $buffer);
+                $current_frame_length = $arr['c1']*4294967296 + $arr['c2'] + 10;
+            } else {
+                $current_frame_length = $data_len + 2;
+            }
+
+            $total_package_size = \strlen($connection->websocketDataBuffer) + $current_frame_length;
+            if ($total_package_size > $connection->maxPackageSize) {
+                Worker::safeEcho("error package. package_length=$total_package_size\n");
+                $connection->close();
+                return 0;
+            }
+
+            if ($is_fin_frame) {
+                if ($opcode === 0x9) {
+                    if ($recv_len >= $current_frame_length) {
+                        $ping_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        if (isset($connection->onWebSocketPing)) {
+                            try {
+                                \call_user_func($connection->onWebSocketPing, $connection, $ping_data);
+                            } catch (\Exception $e) {
+                                Worker::stopAll(250, $e);
+                            } catch (\Error $e) {
+                                Worker::stopAll(250, $e);
+                            }
+                        } else {
+                            $connection->send($ping_data);
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(\substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+
+                } else if ($opcode === 0xa) {
+                    if ($recv_len >= $current_frame_length) {
+                        $pong_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection);
+                        $connection->consumeRecvBuffer($current_frame_length);
+                        $tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
+                        $connection->websocketType = "\x8a";
+                        // Try to emit onWebSocketPong callback.
+                        if (isset($connection->onWebSocketPong)) {
+                            try {
+                                \call_user_func($connection->onWebSocketPong, $connection, $pong_data);
+                            } catch (\Exception $e) {
+                                Worker::stopAll(250, $e);
+                            } catch (\Error $e) {
+                                Worker::stopAll(250, $e);
+                            }
+                        }
+                        $connection->websocketType = $tmp_connection_type;
+                        if ($recv_len > $current_frame_length) {
+                            return static::input(\substr($buffer, $current_frame_length), $connection);
+                        }
+                    }
+                    return 0;
+                }
+                return $current_frame_length;
+            } else {
+                $connection->websocketCurrentFrameLength = $current_frame_length;
+            }
+        }
+        // Received just a frame length data.
+        if ($connection->websocketCurrentFrameLength === $recv_len) {
+            self::decode($buffer, $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $connection->websocketCurrentFrameLength = 0;
+            return 0;
+        } // The length of the received data is greater than the length of a frame.
+        elseif ($connection->websocketCurrentFrameLength < $recv_len) {
+            self::decode(\substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
+            $connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
+            $current_frame_length                    = $connection->websocketCurrentFrameLength;
+            $connection->websocketCurrentFrameLength = 0;
+            // Continue to read next frame.
+            return self::input(\substr($buffer, $current_frame_length), $connection);
+        } // The length of the received data is less than the length of a frame.
+        else {
+            return 0;
+        }
+    }
+
+    /**
+     * Websocket encode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function encode($payload, ConnectionInterface $connection)
+    {
+        if (empty($connection->websocketType)) {
+            $connection->websocketType = self::BINARY_TYPE_BLOB;
+        }
+        $payload = (string)$payload;
+        if (empty($connection->handshakeStep)) {
+            static::sendHandshake($connection);
+        }
+        $mask = 1;
+        $mask_key = "\x00\x00\x00\x00";
+
+        $pack = '';
+        $length = $length_flag = \strlen($payload);
+        if (65535 < $length) {
+            $pack   = \pack('NN', ($length & 0xFFFFFFFF00000000) >> 32, $length & 0x00000000FFFFFFFF);
+            $length_flag = 127;
+        } else if (125 < $length) {
+            $pack   = \pack('n*', $length);
+            $length_flag = 126;
+        }
+
+        $head = ($mask << 7) | $length_flag;
+        $head = $connection->websocketType . \chr($head) . $pack;
+
+        $frame = $head . $mask_key;
+        // append payload to frame:
+        $mask_key = \str_repeat($mask_key, \floor($length / 4)) . \substr($mask_key, 0, $length % 4);
+        $frame .= $payload ^ $mask_key;
+        if ($connection->handshakeStep === 1) {
+            // If buffer has already full then discard the current package.
+            if (\strlen($connection->tmpWebsocketData) > $connection->maxSendBufferSize) {
+                if ($connection->onError) {
+                    try {
+                        \call_user_func($connection->onError, $connection, \WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
+                    } catch (\Exception $e) {
+                        Worker::stopAll(250, $e);
+                    } catch (\Error $e) {
+                        Worker::stopAll(250, $e);
+                    }
+                }
+                return '';
+            }
+            $connection->tmpWebsocketData = $connection->tmpWebsocketData . $frame;
+            // Check buffer is full.
+            if ($connection->maxSendBufferSize <= \strlen($connection->tmpWebsocketData)) {
+                if ($connection->onBufferFull) {
+                    try {
+                        \call_user_func($connection->onBufferFull, $connection);
+                    } catch (\Exception $e) {
+                        Worker::stopAll(250, $e);
+                    } catch (\Error $e) {
+                        Worker::stopAll(250, $e);
+                    }
+                }
+            }
+            return '';
+        }
+        return $frame;
+    }
+
+    /**
+     * Websocket decode.
+     *
+     * @param string              $buffer
+     * @param ConnectionInterface $connection
+     * @return string
+     */
+    public static function decode($bytes, ConnectionInterface $connection)
+    {
+        $data_length = \ord($bytes[1]);
+
+        if ($data_length === 126) {
+            $decoded_data = \substr($bytes, 4);
+        } else if ($data_length === 127) {
+            $decoded_data = \substr($bytes, 10);
+        } else {
+            $decoded_data = \substr($bytes, 2);
+        }
+        if ($connection->websocketCurrentFrameLength) {
+            $connection->websocketDataBuffer .= $decoded_data;
+            return $connection->websocketDataBuffer;
+        } else {
+            if ($connection->websocketDataBuffer !== '') {
+                $decoded_data                    = $connection->websocketDataBuffer . $decoded_data;
+                $connection->websocketDataBuffer = '';
+            }
+            return $decoded_data;
+        }
+    }
+
+    /**
+     * Send websocket handshake data.
+     *
+     * @return void
+     */
+    public static function onConnect($connection)
+    {
+        static::sendHandshake($connection);
+    }
+
+    /**
+     * Clean
+     *
+     * @param TcpConnection $connection
+     */
+    public static function onClose($connection)
+    {
+        $connection->handshakeStep               = null;
+        $connection->websocketCurrentFrameLength = 0;
+        $connection->tmpWebsocketData            = '';
+        $connection->websocketDataBuffer         = '';
+        if (!empty($connection->websocketPingTimer)) {
+            Timer::del($connection->websocketPingTimer);
+            $connection->websocketPingTimer = null;
+        }
+    }
+
+    /**
+     * Send websocket handshake.
+     *
+     * @param TcpConnection $connection
+     * @return void
+     */
+    public static function sendHandshake(TcpConnection $connection)
+    {
+        if (!empty($connection->handshakeStep)) {
+            return;
+        }
+        // Get Host.
+        $port = $connection->getRemotePort();
+        $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
+        // Handshake header.
+        $connection->websocketSecKey = \base64_encode(\md5(\mt_rand(), true));
+        $user_header = isset($connection->headers) ? $connection->headers :
+            (isset($connection->wsHttpHeader) ? $connection->wsHttpHeader : null);
+        $user_header_str = '';
+        if (!empty($user_header)) {
+            if (\is_array($user_header)){
+                foreach($user_header as $k=>$v){
+                    $user_header_str .= "$k: $v\r\n";
+                }
+            } else {
+                $user_header_str .= $user_header;
+            }
+            $user_header_str = "\r\n".\trim($user_header_str);
+        }
+        $header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n".
+        (!\preg_match("/\nHost:/i", $user_header_str) ? "Host: $host\r\n" : '').
+        "Connection: Upgrade\r\n".
+        "Upgrade: websocket\r\n".
+        (isset($connection->websocketOrigin) ? "Origin: ".$connection->websocketOrigin."\r\n":'').
+        (isset($connection->WSClientProtocol)?"Sec-WebSocket-Protocol: ".$connection->WSClientProtocol."\r\n":'').
+        "Sec-WebSocket-Version: 13\r\n".
+        "Sec-WebSocket-Key: " . $connection->websocketSecKey . $user_header_str . "\r\n\r\n";
+        $connection->send($header, true);
+        $connection->handshakeStep               = 1;
+        $connection->websocketCurrentFrameLength = 0;
+        $connection->websocketDataBuffer         = '';
+        $connection->tmpWebsocketData            = '';
+    }
+
+    /**
+     * Websocket handshake.
+     *
+     * @param string                              $buffer
+     * @param TcpConnection $connection
+     * @return int
+     */
+    public static function dealHandshake($buffer, TcpConnection $connection)
+    {
+        $pos = \strpos($buffer, "\r\n\r\n");
+        if ($pos) {
+            //checking Sec-WebSocket-Accept
+            if (\preg_match("/Sec-WebSocket-Accept: *(.*?)\r\n/i", $buffer, $match)) {
+                if ($match[1] !== \base64_encode(\sha1($connection->websocketSecKey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true))) {
+                    Worker::safeEcho("Sec-WebSocket-Accept not match. Header:\n" . \substr($buffer, 0, $pos) . "\n");
+                    $connection->close();
+                    return 0;
+                }
+            } else {
+                Worker::safeEcho("Sec-WebSocket-Accept not found. Header:\n" . \substr($buffer, 0, $pos) . "\n");
+                $connection->close();
+                return 0;
+            }
+
+            // handshake complete
+
+            // Get WebSocket subprotocol (if specified by server)
+            if (\preg_match("/Sec-WebSocket-Protocol: *(.*?)\r\n/i", $buffer, $match)) {
+                $connection->WSServerProtocol = \trim($match[1]);
+            }
+
+            $connection->handshakeStep = 2;
+            $handshake_response_length = $pos + 4;
+            // Try to emit onWebSocketConnect callback.
+            if (isset($connection->onWebSocketConnect)) {
+                try {
+                    \call_user_func($connection->onWebSocketConnect, $connection, \substr($buffer, 0, $handshake_response_length));
+                } catch (\Exception $e) {
+                    Worker::stopAll(250, $e);
+                } catch (\Error $e) {
+                    Worker::stopAll(250, $e);
+                }
+            }
+            // Headbeat.
+            if (!empty($connection->websocketPingInterval)) {
+                $connection->websocketPingTimer = Timer::add($connection->websocketPingInterval, function() use ($connection){
+                    if (false === $connection->send(\pack('H*', '898000000000'), true)) {
+                        Timer::del($connection->websocketPingTimer);
+                        $connection->websocketPingTimer = null;
+                    }
+                });
+            }
+
+            $connection->consumeRecvBuffer($handshake_response_length);
+            if (!empty($connection->tmpWebsocketData)) {
+                $connection->send($connection->tmpWebsocketData, true);
+                $connection->tmpWebsocketData = '';
+            }
+            if (\strlen($buffer) > $handshake_response_length) {
+                return self::input(\substr($buffer, $handshake_response_length), $connection);
+            }
+        }
+        return 0;
+    }
+
+    public static function WSSetProtocol($connection, $params) {
+	$connection->WSClientProtocol = $params[0];
+    }
+
+    public static function WSGetServerProtocol($connection) {
+	return (\property_exists($connection, 'WSServerProtocol') ? $connection->WSServerProtocol : null);
+    }
+
+}
diff --git a/vendor/workerman/workerman/README.md b/vendor/workerman/workerman/README.md
new file mode 100644
index 0000000..856e020
--- /dev/null
+++ b/vendor/workerman/workerman/README.md
@@ -0,0 +1,310 @@
+# Workerman
+[![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)
+[![Latest Stable Version](https://poser.pugx.org/workerman/workerman/v/stable)](https://packagist.org/packages/workerman/workerman)
+[![Total Downloads](https://poser.pugx.org/workerman/workerman/downloads)](https://packagist.org/packages/workerman/workerman)
+[![Monthly Downloads](https://poser.pugx.org/workerman/workerman/d/monthly)](https://packagist.org/packages/workerman/workerman)
+[![Daily Downloads](https://poser.pugx.org/workerman/workerman/d/daily)](https://packagist.org/packages/workerman/workerman)
+[![License](https://poser.pugx.org/workerman/workerman/license)](https://packagist.org/packages/workerman/workerman)
+
+## What is it
+Workerman is an asynchronous event-driven PHP framework with high performance to build fast and scalable network applications. 
+Workerman supports HTTP, Websocket, SSL and other custom protocols. 
+Workerman supports event extension.
+
+## Requires
+PHP 5.3 or Higher  
+A POSIX compatible operating system (Linux, OSX, BSD)  
+POSIX and PCNTL extensions required   
+Event extension recommended for better performance  
+
+## Installation
+
+```
+composer require workerman/workerman
+```
+
+## Basic Usage
+
+### A websocket server 
+```php
+<?php
+
+use Workerman\Worker;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+// Create a Websocket server
+$ws_worker = new Worker('websocket://0.0.0.0:2346');
+
+// Emitted when new connection come
+$ws_worker->onConnect = function ($connection) {
+    echo "New connection\n";
+};
+
+// Emitted when data received
+$ws_worker->onMessage = function ($connection, $data) {
+    // Send hello $data
+    $connection->send('Hello ' . $data);
+};
+
+// Emitted when connection closed
+$ws_worker->onClose = function ($connection) {
+    echo "Connection closed\n";
+};
+
+// Run worker
+Worker::runAll();
+```
+
+### An http server
+```php
+use Workerman\Worker;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+// #### http worker ####
+$http_worker = new Worker('http://0.0.0.0:2345');
+
+// 4 processes
+$http_worker->count = 4;
+
+// Emitted when data received
+$http_worker->onMessage = function ($connection, $request) {
+    //$request->get();
+    //$request->post();
+    //$request->header();
+    //$request->cookie();
+    //$request->session();
+    //$request->uri();
+    //$request->path();
+    //$request->method();
+
+    // Send data to client
+    $connection->send("Hello World");
+};
+
+// Run all workers
+Worker::runAll();
+```
+
+### A tcp server
+```php
+use Workerman\Worker;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+// #### create socket and listen 1234 port ####
+$tcp_worker = new Worker('tcp://0.0.0.0:1234');
+
+// 4 processes
+$tcp_worker->count = 4;
+
+// Emitted when new connection come
+$tcp_worker->onConnect = function ($connection) {
+    echo "New Connection\n";
+};
+
+// Emitted when data received
+$tcp_worker->onMessage = function ($connection, $data) {
+    // Send data to client
+    $connection->send("Hello $data \n");
+};
+
+// Emitted when connection is closed
+$tcp_worker->onClose = function ($connection) {
+    echo "Connection closed\n";
+};
+
+Worker::runAll();
+```
+
+### Enable SSL
+```php
+<?php
+
+use Workerman\Worker;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+// SSL context.
+$context = array(
+    'ssl' => array(
+        'local_cert'  => '/your/path/of/server.pem',
+        'local_pk'    => '/your/path/of/server.key',
+        'verify_peer' => false,
+    )
+);
+
+// Create a Websocket server with ssl context.
+$ws_worker = new Worker('websocket://0.0.0.0:2346', $context);
+
+// Enable SSL. WebSocket+SSL means that Secure WebSocket (wss://). 
+// The similar approaches for Https etc.
+$ws_worker->transport = 'ssl';
+
+$ws_worker->onMessage = function ($connection, $data) {
+    // Send hello $data
+    $connection->send('Hello ' . $data);
+};
+
+Worker::runAll();
+```
+
+### Custom protocol
+Protocols/MyTextProtocol.php
+```php
+
+namespace Protocols;
+
+/**
+ * User defined protocol
+ * Format Text+"\n"
+ */
+class MyTextProtocol
+{
+    public static function input($recv_buffer)
+    {
+        // Find the position of the first occurrence of "\n"
+        $pos = strpos($recv_buffer, "\n");
+
+        // Not a complete package. Return 0 because the length of package can not be calculated
+        if ($pos === false) {
+            return 0;
+        }
+
+        // Return length of the package
+        return $pos+1;
+    }
+
+    public static function decode($recv_buffer)
+    {
+        return trim($recv_buffer);
+    }
+
+    public static function encode($data)
+    {
+        return $data . "\n";
+    }
+}
+```
+
+```php
+use Workerman\Worker;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+// #### MyTextProtocol worker ####
+$text_worker = new Worker('MyTextProtocol://0.0.0.0:5678');
+
+$text_worker->onConnect = function ($connection) {
+    echo "New connection\n";
+};
+
+$text_worker->onMessage = function ($connection, $data) {
+    // Send data to client
+    $connection->send("Hello world\n");
+};
+
+$text_worker->onClose = function ($connection) {
+    echo "Connection closed\n";
+};
+
+// Run all workers
+Worker::runAll();
+```
+
+### Timer
+```php
+
+use Workerman\Worker;
+use Workerman\Timer;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+$task = new Worker();
+$task->onWorkerStart = function ($task) {
+    // 2.5 seconds
+    $time_interval = 2.5; 
+    $timer_id = Timer::add($time_interval, function () {
+        echo "Timer run\n";
+    });
+};
+
+// Run all workers
+Worker::runAll();
+```
+
+### AsyncTcpConnection (tcp/ws/text/frame etc...)
+```php
+
+use Workerman\Worker;
+use Workerman\Connection\AsyncTcpConnection;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+$worker = new Worker();
+$worker->onWorkerStart = function () {
+    // Websocket protocol for client.
+    $ws_connection = new AsyncTcpConnection('ws://echo.websocket.org:80');
+    $ws_connection->onConnect = function ($connection) {
+        $connection->send('Hello');
+    };
+    $ws_connection->onMessage = function ($connection, $data) {
+        echo "Recv: $data\n";
+    };
+    $ws_connection->onError = function ($connection, $code, $msg) {
+        echo "Error: $msg\n";
+    };
+    $ws_connection->onClose = function ($connection) {
+        echo "Connection closed\n";
+    };
+    $ws_connection->connect();
+};
+
+Worker::runAll();
+```
+
+
+
+## Available commands
+```php start.php start  ```  
+```php start.php start -d  ```  
+![workerman start](http://www.workerman.net/img/workerman-start.png)  
+```php start.php status  ```  
+![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123)  
+```php start.php connections```  
+```php start.php stop  ```  
+```php start.php restart  ```  
+```php start.php reload  ```  
+
+## Documentation
+
+中文主页:[http://www.workerman.net](https://www.workerman.net)
+
+中文文档: [https://www.workerman.net/doc/workerman](https://www.workerman.net/doc/workerman)
+
+Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/SUMMARY.md)
+
+# Benchmarks
+https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=db&l=yyku7z-e7&a=2
+![image](https://user-images.githubusercontent.com/6073368/146704320-1559fe97-aa67-4ee3-95d6-61e341b3c93b.png)
+
+## Sponsors
+[opencollective.com/walkor](https://opencollective.com/walkor)
+
+[patreon.com/walkor](https://patreon.com/walkor)
+
+## Donate
+
+<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UQGGS9UB35WWG"><img src="http://donate.workerman.net/img/donate.png"></a>
+
+## Other links with workerman
+
+[webman](https://github.com/walkor/webman)   
+[PHPSocket.IO](https://github.com/walkor/phpsocket.io)   
+[php-socks5](https://github.com/walkor/php-socks5)  
+[php-http-proxy](https://github.com/walkor/php-http-proxy)  
+
+## LICENSE
+
+Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt).
diff --git a/vendor/workerman/workerman/Timer.php b/vendor/workerman/workerman/Timer.php
new file mode 100644
index 0000000..348bb3a
--- /dev/null
+++ b/vendor/workerman/workerman/Timer.php
@@ -0,0 +1,213 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman;
+
+use Workerman\Events\EventInterface;
+use Workerman\Worker;
+use \Exception;
+
+/**
+ * Timer.
+ *
+ * example:
+ * Workerman\Timer::add($time_interval, callback, array($arg1, $arg2..));
+ */
+class Timer
+{
+    /**
+     * Tasks that based on ALARM signal.
+     * [
+     *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
+     *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
+     *   ..
+     * ]
+     *
+     * @var array
+     */
+    protected static $_tasks = array();
+
+    /**
+     * event
+     *
+     * @var EventInterface
+     */
+    protected static $_event = null;
+
+    /**
+     * timer id
+     *
+     * @var int
+     */
+    protected static $_timerId = 0;
+
+    /**
+     * timer status
+     * [
+     *   timer_id1 => bool,
+     *   timer_id2 => bool,
+     *   ....................,
+     * ]
+     *
+     * @var array
+     */
+    protected static $_status = array();
+
+    /**
+     * Init.
+     *
+     * @param EventInterface $event
+     * @return void
+     */
+    public static function init($event = null)
+    {
+        if ($event) {
+            self::$_event = $event;
+            return;
+        }
+        if (\function_exists('pcntl_signal')) {
+            \pcntl_signal(\SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false);
+        }
+    }
+
+    /**
+     * ALARM signal handler.
+     *
+     * @return void
+     */
+    public static function signalHandle()
+    {
+        if (!self::$_event) {
+            \pcntl_alarm(1);
+            self::tick();
+        }
+    }
+
+    /**
+     * Add a timer.
+     *
+     * @param float    $time_interval
+     * @param callable $func
+     * @param mixed    $args
+     * @param bool     $persistent
+     * @return int|bool
+     */
+    public static function add($time_interval, $func, $args = array(), $persistent = true)
+    {
+        if ($time_interval <= 0) {
+            Worker::safeEcho(new Exception("bad time_interval"));
+            return false;
+        }
+
+        if ($args === null) {
+            $args = array();
+        }
+
+        if (self::$_event) {
+            return self::$_event->add($time_interval,
+                $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args);
+        }
+
+        if (!\is_callable($func)) {
+            Worker::safeEcho(new Exception("not callable"));
+            return false;
+        }
+
+        if (empty(self::$_tasks)) {
+            \pcntl_alarm(1);
+        }
+
+        $run_time = \time() + $time_interval;
+        if (!isset(self::$_tasks[$run_time])) {
+            self::$_tasks[$run_time] = array();
+        }
+
+        self::$_timerId = self::$_timerId == \PHP_INT_MAX ? 1 : ++self::$_timerId;
+        self::$_status[self::$_timerId] = true;
+        self::$_tasks[$run_time][self::$_timerId] = array($func, (array)$args, $persistent, $time_interval);
+
+        return self::$_timerId;
+    }
+
+
+    /**
+     * Tick.
+     *
+     * @return void
+     */
+    public static function tick()
+    {
+        if (empty(self::$_tasks)) {
+            \pcntl_alarm(0);
+            return;
+        }
+        $time_now = \time();
+        foreach (self::$_tasks as $run_time => $task_data) {
+            if ($time_now >= $run_time) {
+                foreach ($task_data as $index => $one_task) {
+                    $task_func     = $one_task[0];
+                    $task_args     = $one_task[1];
+                    $persistent    = $one_task[2];
+                    $time_interval = $one_task[3];
+                    try {
+                        \call_user_func_array($task_func, $task_args);
+                    } catch (\Exception $e) {
+                        Worker::safeEcho($e);
+                    }
+                    if($persistent && !empty(self::$_status[$index])) {
+                        $new_run_time = \time() + $time_interval;
+                        if(!isset(self::$_tasks[$new_run_time])) self::$_tasks[$new_run_time] = array();
+                        self::$_tasks[$new_run_time][$index] = array($task_func, (array)$task_args, $persistent, $time_interval);
+                    }
+                }
+                unset(self::$_tasks[$run_time]);
+            }
+        }
+    }
+
+    /**
+     * Remove a timer.
+     *
+     * @param mixed $timer_id
+     * @return bool
+     */
+    public static function del($timer_id)
+    {
+        if (self::$_event) {
+            return self::$_event->del($timer_id, EventInterface::EV_TIMER);
+        }
+
+        foreach(self::$_tasks as $run_time => $task_data) 
+        {
+            if(array_key_exists($timer_id, $task_data)) unset(self::$_tasks[$run_time][$timer_id]);
+        }
+
+        if(array_key_exists($timer_id, self::$_status)) unset(self::$_status[$timer_id]);
+
+        return true;
+    }
+
+    /**
+     * Remove all timers.
+     *
+     * @return void
+     */
+    public static function delAll()
+    {
+        self::$_tasks = self::$_status = array();
+        \pcntl_alarm(0);
+        if (self::$_event) {
+            self::$_event->clearAllTimer();
+        }
+    }
+}
diff --git a/vendor/workerman/workerman/Worker.php b/vendor/workerman/workerman/Worker.php
new file mode 100644
index 0000000..97a7060
--- /dev/null
+++ b/vendor/workerman/workerman/Worker.php
@@ -0,0 +1,2575 @@
+<?php
+/**
+ * This file is part of workerman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Workerman;
+require_once __DIR__ . '/Lib/Constants.php';
+
+use Workerman\Events\EventInterface;
+use Workerman\Connection\ConnectionInterface;
+use Workerman\Connection\TcpConnection;
+use Workerman\Connection\UdpConnection;
+use Workerman\Lib\Timer;
+use Workerman\Events\Select;
+use \Exception;
+
+/**
+ * Worker class
+ * A container for listening ports
+ */
+class Worker
+{
+    /**
+     * Version.
+     *
+     * @var string
+     */
+    const VERSION = '4.0.28';
+
+    /**
+     * Status starting.
+     *
+     * @var int
+     */
+    const STATUS_STARTING = 1;
+
+    /**
+     * Status running.
+     *
+     * @var int
+     */
+    const STATUS_RUNNING = 2;
+
+    /**
+     * Status shutdown.
+     *
+     * @var int
+     */
+    const STATUS_SHUTDOWN = 4;
+
+    /**
+     * Status reloading.
+     *
+     * @var int
+     */
+    const STATUS_RELOADING = 8;
+
+    /**
+     * After sending the restart command to the child process KILL_WORKER_TIMER_TIME seconds,
+     * if the process is still living then forced to kill.
+     *
+     * @var int
+     */
+    const KILL_WORKER_TIMER_TIME = 2;
+
+    /**
+     * Default backlog. Backlog is the maximum length of the queue of pending connections.
+     *
+     * @var int
+     */
+    const DEFAULT_BACKLOG = 102400;
+    /**
+     * Max udp package size.
+     *
+     * @var int
+     */
+    const MAX_UDP_PACKAGE_SIZE = 65535;
+
+    /**
+     * The safe distance for columns adjacent
+     *
+     * @var int
+     */
+    const UI_SAFE_LENGTH = 4;
+
+    /**
+     * Worker id.
+     *
+     * @var int
+     */
+    public $id = 0;
+
+    /**
+     * Name of the worker processes.
+     *
+     * @var string
+     */
+    public $name = 'none';
+
+    /**
+     * Number of worker processes.
+     *
+     * @var int
+     */
+    public $count = 1;
+
+    /**
+     * Unix user of processes, needs appropriate privileges (usually root).
+     *
+     * @var string
+     */
+    public $user = '';
+
+    /**
+     * Unix group of processes, needs appropriate privileges (usually root).
+     *
+     * @var string
+     */
+    public $group = '';
+
+    /**
+     * reloadable.
+     *
+     * @var bool
+     */
+    public $reloadable = true;
+
+    /**
+     * reuse port.
+     *
+     * @var bool
+     */
+    public $reusePort = false;
+
+    /**
+     * Emitted when worker processes start.
+     *
+     * @var callable
+     */
+    public $onWorkerStart = null;
+
+    /**
+     * Emitted when a socket connection is successfully established.
+     *
+     * @var callable
+     */
+    public $onConnect = null;
+
+    /**
+     * Emitted when data is received.
+     *
+     * @var callable
+     */
+    public $onMessage = null;
+
+    /**
+     * Emitted when the other end of the socket sends a FIN packet.
+     *
+     * @var callable
+     */
+    public $onClose = null;
+
+    /**
+     * Emitted when an error occurs with connection.
+     *
+     * @var callable
+     */
+    public $onError = null;
+
+    /**
+     * Emitted when the send buffer becomes full.
+     *
+     * @var callable
+     */
+    public $onBufferFull = null;
+
+    /**
+     * Emitted when the send buffer becomes empty.
+     *
+     * @var callable
+     */
+    public $onBufferDrain = null;
+
+    /**
+     * Emitted when worker processes stoped.
+     *
+     * @var callable
+     */
+    public $onWorkerStop = null;
+
+    /**
+     * Emitted when worker processes get reload signal.
+     *
+     * @var callable
+     */
+    public $onWorkerReload = null;
+
+    /**
+     * Transport layer protocol.
+     *
+     * @var string
+     */
+    public $transport = 'tcp';
+
+    /**
+     * Store all connections of clients.
+     *
+     * @var array
+     */
+    public $connections = array();
+
+    /**
+     * Application layer protocol.
+     *
+     * @var string
+     */
+    public $protocol = null;
+
+    /**
+     * Root path for autoload.
+     *
+     * @var string
+     */
+    protected $_autoloadRootPath = '';
+
+    /**
+     * Pause accept new connections or not.
+     *
+     * @var bool
+     */
+    protected $_pauseAccept = true;
+
+    /**
+     * Is worker stopping ?
+     * @var bool
+     */
+    public $stopping = false;
+
+    /**
+     * Daemonize.
+     *
+     * @var bool
+     */
+    public static $daemonize = false;
+
+    /**
+     * Stdout file.
+     *
+     * @var string
+     */
+    public static $stdoutFile = '/dev/null';
+
+    /**
+     * The file to store master process PID.
+     *
+     * @var string
+     */
+    public static $pidFile = '';
+
+    /**
+     * The file used to store the master process status file.
+     *
+     * @var string
+     */
+    public static $statusFile = '';
+    
+    /**
+     * Log file.
+     *
+     * @var mixed
+     */
+    public static $logFile = '';
+
+    /**
+     * Global event loop.
+     *
+     * @var EventInterface
+     */
+    public static $globalEvent = null;
+
+    /**
+     * Emitted when the master process get reload signal.
+     *
+     * @var callable
+     */
+    public static $onMasterReload = null;
+
+    /**
+     * Emitted when the master process terminated.
+     *
+     * @var callable
+     */
+    public static $onMasterStop = null;
+
+    /**
+     * EventLoopClass
+     *
+     * @var string
+     */
+    public static $eventLoopClass = '';
+
+    /**
+     * Process title
+     *
+     * @var string
+     */
+    public static $processTitle = 'WorkerMan';
+
+    /**
+     * The PID of master process.
+     *
+     * @var int
+     */
+    protected static $_masterPid = 0;
+
+    /**
+     * Listening socket.
+     *
+     * @var resource
+     */
+    protected $_mainSocket = null;
+
+    /**
+     * Socket name. The format is like this http://0.0.0.0:80 .
+     *
+     * @var string
+     */
+    protected $_socketName = '';
+
+    /** parse from _socketName avoid parse again in master or worker
+     * LocalSocket The format is like tcp://0.0.0.0:8080
+     * @var string
+     */
+
+    protected $_localSocket=null;
+
+    /**
+     * Context of socket.
+     *
+     * @var resource
+     */
+    protected $_context = null;
+
+    /**
+     * All worker instances.
+     *
+     * @var Worker[]
+     */
+    protected static $_workers = array();
+
+    /**
+     * All worker processes pid.
+     * The format is like this [worker_id=>[pid=>pid, pid=>pid, ..], ..]
+     *
+     * @var array
+     */
+    protected static $_pidMap = array();
+
+    /**
+     * All worker processes waiting for restart.
+     * The format is like this [pid=>pid, pid=>pid].
+     *
+     * @var array
+     */
+    protected static $_pidsToRestart = array();
+
+    /**
+     * Mapping from PID to worker process ID.
+     * The format is like this [worker_id=>[0=>$pid, 1=>$pid, ..], ..].
+     *
+     * @var array
+     */
+    protected static $_idMap = array();
+
+    /**
+     * Current status.
+     *
+     * @var int
+     */
+    protected static $_status = self::STATUS_STARTING;
+
+    /**
+     * Maximum length of the worker names.
+     *
+     * @var int
+     */
+    protected static $_maxWorkerNameLength = 12;
+
+    /**
+     * Maximum length of the socket names.
+     *
+     * @var int
+     */
+    protected static $_maxSocketNameLength = 12;
+
+    /**
+     * Maximum length of the process user names.
+     *
+     * @var int
+     */
+    protected static $_maxUserNameLength = 12;
+
+    /**
+     * Maximum length of the Proto names.
+     *
+     * @var int
+     */
+    protected static $_maxProtoNameLength = 4;
+
+    /**
+     * Maximum length of the Processes names.
+     *
+     * @var int
+     */
+    protected static $_maxProcessesNameLength = 9;
+
+    /**
+     * Maximum length of the Status names.
+     *
+     * @var int
+     */
+    protected static $_maxStatusNameLength = 1;
+
+    /**
+     * The file to store status info of current worker process.
+     *
+     * @var string
+     */
+    protected static $_statisticsFile = '';
+
+    /**
+     * Start file.
+     *
+     * @var string
+     */
+    protected static $_startFile = '';
+
+    /**
+     * OS.
+     *
+     * @var string
+     */
+    protected static $_OS = \OS_TYPE_LINUX;
+
+    /**
+     * Processes for windows.
+     *
+     * @var array
+     */
+    protected static $_processForWindows = array();
+
+    /**
+     * Status info of current worker process.
+     *
+     * @var array
+     */
+    protected static $_globalStatistics = array(
+        'start_timestamp'  => 0,
+        'worker_exit_info' => array()
+    );
+
+    /**
+     * Available event loops.
+     *
+     * @var array
+     */
+    protected static $_availableEventLoops = array(
+        'event'    => '\Workerman\Events\Event',
+        'libevent' => '\Workerman\Events\Libevent'
+    );
+
+    /**
+     * PHP built-in protocols.
+     *
+     * @var array
+     */
+    protected static $_builtinTransports = array(
+        'tcp'   => 'tcp',
+        'udp'   => 'udp',
+        'unix'  => 'unix',
+        'ssl'   => 'tcp'
+    );
+
+    /**
+     * PHP built-in error types.
+     *
+     * @var array
+     */
+    protected static $_errorType = array(
+        \E_ERROR             => 'E_ERROR',             // 1
+        \E_WARNING           => 'E_WARNING',           // 2
+        \E_PARSE             => 'E_PARSE',             // 4
+        \E_NOTICE            => 'E_NOTICE',            // 8
+        \E_CORE_ERROR        => 'E_CORE_ERROR',        // 16
+        \E_CORE_WARNING      => 'E_CORE_WARNING',      // 32
+        \E_COMPILE_ERROR     => 'E_COMPILE_ERROR',     // 64
+        \E_COMPILE_WARNING   => 'E_COMPILE_WARNING',   // 128
+        \E_USER_ERROR        => 'E_USER_ERROR',        // 256
+        \E_USER_WARNING      => 'E_USER_WARNING',      // 512
+        \E_USER_NOTICE       => 'E_USER_NOTICE',       // 1024
+        \E_STRICT            => 'E_STRICT',            // 2048
+        \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', // 4096
+        \E_DEPRECATED        => 'E_DEPRECATED',        // 8192
+        \E_USER_DEPRECATED   => 'E_USER_DEPRECATED'   // 16384
+    );
+
+    /**
+     * Graceful stop or not.
+     *
+     * @var bool
+     */
+    protected static $_gracefulStop = false;
+
+    /**
+     * Standard output stream
+     * @var resource
+     */
+    protected static $_outputStream = null;
+
+    /**
+     * If $outputStream support decorated
+     * @var bool
+     */
+    protected static $_outputDecorated = null;
+
+    /**
+     * Run all worker instances.
+     *
+     * @return void
+     */
+    public static function runAll()
+    {
+        static::checkSapiEnv();
+        static::init();
+        static::parseCommand();
+        static::daemonize();
+        static::initWorkers();
+        static::installSignal();
+        static::saveMasterPid();
+        static::displayUI();
+        static::forkWorkers();
+        static::resetStd();
+        static::monitorWorkers();
+    }
+
+    /**
+     * Check sapi.
+     *
+     * @return void
+     */
+    protected static function checkSapiEnv()
+    {
+        // Only for cli.
+        if (\PHP_SAPI !== 'cli') {
+            exit("Only run in command line mode \n");
+        }
+        if (\DIRECTORY_SEPARATOR === '\\') {
+            self::$_OS = \OS_TYPE_WINDOWS;
+        }
+    }
+
+    /**
+     * Init.
+     *
+     * @return void
+     */
+    protected static function init()
+    {
+        \set_error_handler(function($code, $msg, $file, $line){
+            Worker::safeEcho("$msg in file $file on line $line\n");
+        });
+
+        // Start file.
+        $backtrace        = \debug_backtrace();
+        static::$_startFile = $backtrace[\count($backtrace) - 1]['file'];
+
+
+        $unique_prefix = \str_replace('/', '_', static::$_startFile);
+
+        // Pid file.
+        if (empty(static::$pidFile)) {
+            static::$pidFile = __DIR__ . "/../$unique_prefix.pid";
+        }
+
+        // Log file.
+        if (empty(static::$logFile)) {
+            static::$logFile = __DIR__ . '/../workerman.log';
+        }
+        $log_file = (string)static::$logFile;
+        if (!\is_file($log_file)) {
+            \touch($log_file);
+            \chmod($log_file, 0622);
+        }
+
+        // State.
+        static::$_status = static::STATUS_STARTING;
+
+        // For statistics.
+        static::$_globalStatistics['start_timestamp'] = \time();
+
+        // Process title.
+        static::setProcessTitle(static::$processTitle . ': master process  start_file=' . static::$_startFile);
+
+        // Init data for worker id.
+        static::initId();
+
+        // Timer init.
+        Timer::init();
+    }
+
+    /**
+     * Lock.
+     *
+     * @return void
+     */
+    protected static function lock()
+    {
+        $fd = \fopen(static::$_startFile, 'r');
+        if ($fd && !flock($fd, LOCK_EX)) {
+            static::log('Workerman['.static::$_startFile.'] already running.');
+            exit;
+        }
+    }
+
+    /**
+     * Unlock.
+     *
+     * @return void
+     */
+    protected static function unlock()
+    {
+        $fd = \fopen(static::$_startFile, 'r');
+        $fd && flock($fd, \LOCK_UN);
+    }
+
+    /**
+     * Init All worker instances.
+     *
+     * @return void
+     */
+    protected static function initWorkers()
+    {
+        if (static::$_OS !== \OS_TYPE_LINUX) {
+            return;
+        }
+        
+        static::$_statisticsFile =  static::$statusFile ? static::$statusFile : __DIR__ . '/../workerman-' .posix_getpid().'.status';
+
+        foreach (static::$_workers as $worker) {
+            // Worker name.
+            if (empty($worker->name)) {
+                $worker->name = 'none';
+            }
+
+            // Get unix user of the worker process.
+            if (empty($worker->user)) {
+                $worker->user = static::getCurrentUser();
+            } else {
+                if (\posix_getuid() !== 0 && $worker->user !== static::getCurrentUser()) {
+                    static::log('Warning: You must have the root privileges to change uid and gid.');
+                }
+            }
+
+            // Socket name.
+            $worker->socket = $worker->getSocketName();
+
+            // Status name.
+            $worker->status = '<g> [OK] </g>';
+
+            // Get column mapping for UI
+            foreach(static::getUiColumns() as $column_name => $prop){
+                !isset($worker->{$prop}) && $worker->{$prop} = 'NNNN';
+                $prop_length = \strlen($worker->{$prop});
+                $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength';
+                static::$$key = \max(static::$$key, $prop_length);
+            }
+
+            // Listen.
+            if (!$worker->reusePort) {
+                $worker->listen();
+            }
+        }
+    }
+
+    /**
+     * Reload all worker instances.
+     *
+     * @return void
+     */
+    public static function reloadAllWorkers()
+    {
+        static::init();
+        static::initWorkers();
+        static::displayUI();
+        static::$_status = static::STATUS_RELOADING;
+    }
+
+    /**
+     * Get all worker instances.
+     *
+     * @return array
+     */
+    public static function getAllWorkers()
+    {
+        return static::$_workers;
+    }
+
+    /**
+     * Get global event-loop instance.
+     *
+     * @return EventInterface
+     */
+    public static function getEventLoop()
+    {
+        return static::$globalEvent;
+    }
+
+    /**
+     * Get main socket resource
+     * @return resource
+     */
+    public function getMainSocket(){
+        return $this->_mainSocket;
+    }
+
+    /**
+     * Init idMap.
+     * return void
+     */
+    protected static function initId()
+    {
+        foreach (static::$_workers as $worker_id => $worker) {
+            $new_id_map = array();
+            $worker->count = $worker->count < 1 ? 1 : $worker->count;
+            for($key = 0; $key < $worker->count; $key++) {
+                $new_id_map[$key] = isset(static::$_idMap[$worker_id][$key]) ? static::$_idMap[$worker_id][$key] : 0;
+            }
+            static::$_idMap[$worker_id] = $new_id_map;
+        }
+    }
+
+    /**
+     * Get unix user of current porcess.
+     *
+     * @return string
+     */
+    protected static function getCurrentUser()
+    {
+        $user_info = \posix_getpwuid(\posix_getuid());
+        return $user_info['name'];
+    }
+
+    /**
+     * Display staring UI.
+     *
+     * @return void
+     */
+    protected static function displayUI()
+    {
+        global $argv;
+        if (\in_array('-q', $argv)) {
+            return;
+        }
+        if (static::$_OS !== \OS_TYPE_LINUX) {
+            static::safeEcho("----------------------- WORKERMAN -----------------------------\r\n");
+            static::safeEcho('Workerman version:'. static::VERSION. '          PHP version:'. \PHP_VERSION. "\r\n");
+            static::safeEcho("------------------------ WORKERS -------------------------------\r\n");
+            static::safeEcho("worker                        listen                              processes status\r\n");
+            return;
+        }
+
+        //show version
+        $line_version = 'Workerman version:' . static::VERSION . \str_pad('PHP version:', 22, ' ', \STR_PAD_LEFT) . \PHP_VERSION . \PHP_EOL;
+        !\defined('LINE_VERSIOIN_LENGTH') && \define('LINE_VERSIOIN_LENGTH', \strlen($line_version));
+        $total_length = static::getSingleLineTotalLength();
+        $line_one = '<n>' . \str_pad('<w> WORKERMAN </w>', $total_length + \strlen('<w></w>'), '-', \STR_PAD_BOTH) . '</n>'. \PHP_EOL;
+        $line_two = \str_pad('<w> WORKERS </w>' , $total_length  + \strlen('<w></w>'), '-', \STR_PAD_BOTH) . \PHP_EOL;
+        static::safeEcho($line_one . $line_version . $line_two);
+
+        //Show title
+        $title = '';
+        foreach(static::getUiColumns() as $column_name => $prop){
+            $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength';
+            //just keep compatible with listen name
+            $column_name === 'socket' && $column_name = 'listen';
+            $title.= "<w>{$column_name}</w>"  .  \str_pad('', static::$$key + static::UI_SAFE_LENGTH - \strlen($column_name));
+        }
+        $title && static::safeEcho($title . \PHP_EOL);
+
+        //Show content
+        foreach (static::$_workers as $worker) {
+            $content = '';
+            foreach(static::getUiColumns() as $column_name => $prop){
+                $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength';
+                \preg_match_all("/(<n>|<\/n>|<w>|<\/w>|<g>|<\/g>)/is", $worker->{$prop}, $matches);
+                $place_holder_length = !empty($matches) ? \strlen(\implode('', $matches[0])) : 0;
+                $content .= \str_pad($worker->{$prop}, static::$$key + static::UI_SAFE_LENGTH + $place_holder_length);
+            }
+            $content && static::safeEcho($content . \PHP_EOL);
+        }
+
+        //Show last line
+        $line_last = \str_pad('', static::getSingleLineTotalLength(), '-') . \PHP_EOL;
+        !empty($content) && static::safeEcho($line_last);
+
+        if (static::$daemonize) {
+            $tmpArgv = $argv;
+            foreach ($tmpArgv as $index => $value) {
+                if ($value == '-d') {
+                    unset($tmpArgv[$index]);
+                } elseif ($value == 'start' || $value == 'restart') {
+                    $tmpArgv[$index] = 'stop';
+                }
+            }
+            static::safeEcho("Input \"php ".implode(' ', $tmpArgv)."\" to stop. Start success.\n\n");
+        } else {
+            static::safeEcho("Press Ctrl+C to stop. Start success.\n");
+        }
+    }
+
+    /**
+     * Get UI columns to be shown in terminal
+     *
+     * 1. $column_map: array('ui_column_name' => 'clas_property_name')
+     * 2. Consider move into configuration in future
+     *
+     * @return array
+     */
+    public static function getUiColumns()
+    {
+        return array(
+            'proto'     =>  'transport',
+            'user'      =>  'user',
+            'worker'    =>  'name',
+            'socket'    =>  'socket',
+            'processes' =>  'count',
+            'status'    =>  'status',
+        );
+    }
+
+    /**
+     * Get single line total length for ui
+     *
+     * @return int
+     */
+    public static function getSingleLineTotalLength()
+    {
+        $total_length = 0;
+
+        foreach(static::getUiColumns() as $column_name => $prop){
+            $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength';
+            $total_length += static::$$key + static::UI_SAFE_LENGTH;
+        }
+
+        //keep beauty when show less colums
+        !\defined('LINE_VERSIOIN_LENGTH') && \define('LINE_VERSIOIN_LENGTH', 0);
+        $total_length <= LINE_VERSIOIN_LENGTH && $total_length = LINE_VERSIOIN_LENGTH;
+
+        return $total_length;
+    }
+
+    /**
+     * Parse command.
+     *
+     * @return void
+     */
+    protected static function parseCommand()
+    {
+        if (static::$_OS !== \OS_TYPE_LINUX) {
+            return;
+        }
+        global $argv;
+        // Check argv;
+        $start_file = $argv[0];
+        $usage = "Usage: php yourfile <command> [mode]\nCommands: \nstart\t\tStart worker in DEBUG mode.\n\t\tUse mode -d to start in DAEMON mode.\nstop\t\tStop worker.\n\t\tUse mode -g to stop gracefully.\nrestart\t\tRestart workers.\n\t\tUse mode -d to start in DAEMON mode.\n\t\tUse mode -g to stop gracefully.\nreload\t\tReload codes.\n\t\tUse mode -g to reload gracefully.\nstatus\t\tGet worker status.\n\t\tUse mode -d to show live status.\nconnections\tGet worker connections.\n";
+        $available_commands = array(
+            'start',
+            'stop',
+            'restart',
+            'reload',
+            'status',
+            'connections',
+        );
+        $available_mode = array(
+            '-d',
+            '-g'
+        );
+        $command = $mode = '';
+        foreach ($argv as $value) {
+            if (\in_array($value, $available_commands)) {
+                $command = $value;
+            } elseif (\in_array($value, $available_mode)) {
+                $mode = $value;
+            }
+        }
+
+        if (!$command) {
+            exit($usage);
+        }
+
+        // Start command.
+        $mode_str = '';
+        if ($command === 'start') {
+            if ($mode === '-d' || static::$daemonize) {
+                $mode_str = 'in DAEMON mode';
+            } else {
+                $mode_str = 'in DEBUG mode';
+            }
+        }
+        static::log("Workerman[$start_file] $command $mode_str");
+
+        // Get master process PID.
+        $master_pid      = \is_file(static::$pidFile) ? (int)\file_get_contents(static::$pidFile) : 0;
+        // Master is still alive?
+        if (static::checkMasterIsAlive($master_pid)) {
+            if ($command === 'start') {
+                static::log("Workerman[$start_file] already running");
+                exit;
+            }
+        } elseif ($command !== 'start' && $command !== 'restart') {
+            static::log("Workerman[$start_file] not run");
+            exit;
+        }
+
+        $statistics_file =  static::$statusFile ? static::$statusFile : __DIR__ . "/../workerman-$master_pid.status";
+
+        // execute command.
+        switch ($command) {
+            case 'start':
+                if ($mode === '-d') {
+                    static::$daemonize = true;
+                }
+                break;
+            case 'status':
+                while (1) {
+                    if (\is_file($statistics_file)) {
+                        @\unlink($statistics_file);
+                    }
+                    // Master process will send SIGUSR2 signal to all child processes.
+                    \posix_kill($master_pid, SIGUSR2);
+                    // Sleep 1 second.
+                    \sleep(1);
+                    // Clear terminal.
+                    if ($mode === '-d') {
+                        static::safeEcho("\33[H\33[2J\33(B\33[m", true);
+                    }
+                    // Echo status data.
+                    static::safeEcho(static::formatStatusData($statistics_file));
+                    if ($mode !== '-d') {
+                        exit(0);
+                    }
+                    static::safeEcho("\nPress Ctrl+C to quit.\n\n");
+                }
+                exit(0);
+            case 'connections':
+                if (\is_file($statistics_file) && \is_writable($statistics_file)) {
+                    \unlink($statistics_file);
+                }
+                // Master process will send SIGIO signal to all child processes.
+                \posix_kill($master_pid, SIGIO);
+                // Waiting amoment.
+                \usleep(500000);
+                // Display statisitcs data from a disk file.
+                if(\is_readable($statistics_file)) {
+                    \readfile($statistics_file);
+                }
+                exit(0);
+            case 'restart':
+            case 'stop':
+                if ($mode === '-g') {
+                    static::$_gracefulStop = true;
+                    $sig = \SIGHUP;
+                    static::log("Workerman[$start_file] is gracefully stopping ...");
+                } else {
+                    static::$_gracefulStop = false;
+                    $sig = \SIGINT;
+                    static::log("Workerman[$start_file] is stopping ...");
+                }
+                // Send stop signal to master process.
+                $master_pid && \posix_kill($master_pid, $sig);
+                // Timeout.
+                $timeout    = 5;
+                $start_time = \time();
+                // Check master process is still alive?
+                while (1) {
+                    $master_is_alive = $master_pid && \posix_kill((int) $master_pid, 0);
+                    if ($master_is_alive) {
+                        // Timeout?
+                        if (!static::$_gracefulStop && \time() - $start_time >= $timeout) {
+                            static::log("Workerman[$start_file] stop fail");
+                            exit;
+                        }
+                        // Waiting amoment.
+                        \usleep(10000);
+                        continue;
+                    }
+                    // Stop success.
+                    static::log("Workerman[$start_file] stop success");
+                    if ($command === 'stop') {
+                        exit(0);
+                    }
+                    if ($mode === '-d') {
+                        static::$daemonize = true;
+                    }
+                    break;
+                }
+                break;
+            case 'reload':
+                if($mode === '-g'){
+                    $sig = \SIGQUIT;
+                }else{
+                    $sig = \SIGUSR1;
+                }
+                \posix_kill($master_pid, $sig);
+                exit;
+            default :
+                if (isset($command)) {
+                    static::safeEcho('Unknown command: ' . $command . "\n");
+                }
+                exit($usage);
+        }
+    }
+
+    /**
+     * Format status data.
+     *
+     * @param $statistics_file
+     * @return string
+     */
+    protected static function formatStatusData($statistics_file)
+    {
+        static $total_request_cache = array();
+        if (!\is_readable($statistics_file)) {
+            return '';
+        }
+        $info = \file($statistics_file, \FILE_IGNORE_NEW_LINES);
+        if (!$info) {
+            return '';
+        }
+        $status_str = '';
+        $current_total_request = array();
+        $worker_info = \unserialize($info[0]);
+        \ksort($worker_info, SORT_NUMERIC);
+        unset($info[0]);
+        $data_waiting_sort = array();
+        $read_process_status = false;
+        $total_requests = 0;
+        $total_qps = 0;
+        $total_connections = 0;
+        $total_fails = 0;
+        $total_memory = 0;
+        $total_timers = 0;
+        $maxLen1 = static::$_maxSocketNameLength;
+        $maxLen2 = static::$_maxWorkerNameLength;
+        foreach($info as $key => $value) {
+            if (!$read_process_status) {
+                $status_str .= $value . "\n";
+                if (\preg_match('/^pid.*?memory.*?listening/', $value)) {
+                    $read_process_status = true;
+                }
+                continue;
+            }
+            if(\preg_match('/^[0-9]+/', $value, $pid_math)) {
+                $pid = $pid_math[0];
+                $data_waiting_sort[$pid] = $value;
+                if(\preg_match('/^\S+?\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?(\S+?)\s+?/', $value, $match)) {
+                    $total_memory += \intval(\str_ireplace('M','',$match[1]));
+                    $maxLen1 = \max($maxLen1,\strlen($match[2]));
+                    $maxLen2 = \max($maxLen2,\strlen($match[3]));
+                    $total_connections += \intval($match[4]);
+                    $total_fails += \intval($match[5]);
+                    $total_timers += \intval($match[6]);
+                    $current_total_request[$pid] = $match[7];
+                    $total_requests += \intval($match[7]);
+                }
+            }
+        }
+        foreach($worker_info as $pid => $info) {
+            if (!isset($data_waiting_sort[$pid])) {
+                $status_str .= "$pid\t" . \str_pad('N/A', 7) . " "
+                    . \str_pad($info['listen'], static::$_maxSocketNameLength) . " "
+                    . \str_pad($info['name'], static::$_maxWorkerNameLength) . " "
+                    . \str_pad('N/A', 11) . " " . \str_pad('N/A', 9) . " "
+                    . \str_pad('N/A', 7) . " " . \str_pad('N/A', 13) . " N/A    [busy] \n";
+                continue;
+            }
+            //$qps = isset($total_request_cache[$pid]) ? $current_total_request[$pid]
+            if (!isset($total_request_cache[$pid]) || !isset($current_total_request[$pid])) {
+                $qps = 0;
+            } else {
+                $qps = $current_total_request[$pid] - $total_request_cache[$pid];
+                $total_qps += $qps;
+            }
+            $status_str .= $data_waiting_sort[$pid]. " " . \str_pad($qps, 6) ." [idle]\n";
+        }
+        $total_request_cache = $current_total_request;
+        $status_str .= "----------------------------------------------PROCESS STATUS---------------------------------------------------\n";
+        $status_str .= "Summary\t" . \str_pad($total_memory.'M', 7) . " "
+            . \str_pad('-', $maxLen1) . " "
+            . \str_pad('-', $maxLen2) . " "
+            . \str_pad($total_connections, 11) . " " . \str_pad($total_fails, 9) . " "
+            . \str_pad($total_timers, 7) . " " . \str_pad($total_requests, 13) . " "
+            . \str_pad($total_qps,6)." [Summary] \n";
+        return $status_str;
+    }
+
+
+    /**
+     * Install signal handler.
+     *
+     * @return void
+     */
+    protected static function installSignal()
+    {
+        if (static::$_OS !== \OS_TYPE_LINUX) {
+            return;
+        }
+        $signalHandler = '\Workerman\Worker::signalHandler';
+        // stop
+        \pcntl_signal(\SIGINT, $signalHandler, false);
+        // stop
+        \pcntl_signal(\SIGTERM, $signalHandler, false);
+        // graceful stop
+        \pcntl_signal(\SIGHUP, $signalHandler, false);
+        // reload
+        \pcntl_signal(\SIGUSR1, $signalHandler, false);
+        // graceful reload
+        \pcntl_signal(\SIGQUIT, $signalHandler, false);
+        // status
+        \pcntl_signal(\SIGUSR2, $signalHandler, false);
+        // connection status
+        \pcntl_signal(\SIGIO, $signalHandler, false);
+        // ignore
+        \pcntl_signal(\SIGPIPE, \SIG_IGN, false);
+    }
+
+    /**
+     * Reinstall signal handler.
+     *
+     * @return void
+     */
+    protected static function reinstallSignal()
+    {
+        if (static::$_OS !== \OS_TYPE_LINUX) {
+            return;
+        }
+        $signalHandler = '\Workerman\Worker::signalHandler';
+        // uninstall stop signal handler
+        \pcntl_signal(\SIGINT, \SIG_IGN, false);
+        // uninstall stop signal handler
+        \pcntl_signal(\SIGTERM, \SIG_IGN, false);
+        // uninstall graceful stop signal handler
+        \pcntl_signal(\SIGHUP, \SIG_IGN, false);
+        // uninstall reload signal handler
+        \pcntl_signal(\SIGUSR1, \SIG_IGN, false);
+        // uninstall graceful reload signal handler
+        \pcntl_signal(\SIGQUIT, \SIG_IGN, false);
+        // uninstall status signal handler
+        \pcntl_signal(\SIGUSR2, \SIG_IGN, false);
+        // uninstall connections status signal handler
+        \pcntl_signal(\SIGIO, \SIG_IGN, false);
+        // reinstall stop signal handler
+        static::$globalEvent->add(\SIGINT, EventInterface::EV_SIGNAL, $signalHandler);
+        // reinstall graceful stop signal handler
+        static::$globalEvent->add(\SIGHUP, EventInterface::EV_SIGNAL, $signalHandler);
+        // reinstall reload signal handler
+        static::$globalEvent->add(\SIGUSR1, EventInterface::EV_SIGNAL, $signalHandler);
+        // reinstall graceful reload signal handler
+        static::$globalEvent->add(\SIGQUIT, EventInterface::EV_SIGNAL, $signalHandler);
+        // reinstall status signal handler
+        static::$globalEvent->add(\SIGUSR2, EventInterface::EV_SIGNAL, $signalHandler);
+        // reinstall connection status signal handler
+        static::$globalEvent->add(\SIGIO, EventInterface::EV_SIGNAL, $signalHandler);
+    }
+
+    /**
+     * Signal handler.
+     *
+     * @param int $signal
+     */
+    public static function signalHandler($signal)
+    {
+        switch ($signal) {
+            // Stop.
+            case \SIGINT:
+            case \SIGTERM:
+                static::$_gracefulStop = false;
+                static::stopAll();
+                break;
+            // Graceful stop.
+            case \SIGHUP:
+                static::$_gracefulStop = true;
+                static::stopAll();
+                break;
+            // Reload.
+            case \SIGQUIT:
+            case \SIGUSR1:
+                static::$_gracefulStop = $signal === \SIGQUIT;
+                static::$_pidsToRestart = static::getAllWorkerPids();
+                static::reload();
+                break;
+            // Show status.
+            case \SIGUSR2:
+                static::writeStatisticsToStatusFile();
+                break;
+            // Show connection status.
+            case \SIGIO:
+                static::writeConnectionsStatisticsToStatusFile();
+                break;
+        }
+    }
+
+    /**
+     * Run as daemon mode.
+     *
+     * @throws Exception
+     */
+    protected static function daemonize()
+    {
+        if (!static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) {
+            return;
+        }
+        \umask(0);
+        $pid = \pcntl_fork();
+        if (-1 === $pid) {
+            throw new Exception('Fork fail');
+        } elseif ($pid > 0) {
+            exit(0);
+        }
+        if (-1 === \posix_setsid()) {
+            throw new Exception("Setsid fail");
+        }
+        // Fork again avoid SVR4 system regain the control of terminal.
+        $pid = \pcntl_fork();
+        if (-1 === $pid) {
+            throw new Exception("Fork fail");
+        } elseif (0 !== $pid) {
+            exit(0);
+        }
+    }
+
+    /**
+     * Redirect standard input and output.
+     *
+     * @throws Exception
+     */
+    public static function resetStd()
+    {
+        if (!static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) {
+            return;
+        }
+        global $STDOUT, $STDERR;
+        $handle = \fopen(static::$stdoutFile, "a");
+        if ($handle) {
+            unset($handle);
+            \set_error_handler(function(){});
+            if ($STDOUT) {
+                \fclose($STDOUT);
+            }
+            if ($STDERR) {
+                \fclose($STDERR);
+            }
+            \fclose(\STDOUT);
+            \fclose(\STDERR);
+            $STDOUT = \fopen(static::$stdoutFile, "a");
+            $STDERR = \fopen(static::$stdoutFile, "a");
+            // change output stream
+            static::$_outputStream = null;
+            static::outputStream($STDOUT);
+            \restore_error_handler();
+            return;
+        }
+
+        throw new Exception('Can not open stdoutFile ' . static::$stdoutFile);
+    }
+
+    /**
+     * Save pid.
+     *
+     * @throws Exception
+     */
+    protected static function saveMasterPid()
+    {
+        if (static::$_OS !== \OS_TYPE_LINUX) {
+            return;
+        }
+
+        static::$_masterPid = \posix_getpid();
+        if (false === \file_put_contents(static::$pidFile, static::$_masterPid)) {
+            throw new Exception('can not save pid to ' . static::$pidFile);
+        }
+    }
+
+    /**
+     * Get event loop name.
+     *
+     * @return string
+     */
+    protected static function getEventLoopName()
+    {
+        if (static::$eventLoopClass) {
+            return static::$eventLoopClass;
+        }
+
+        if (!\class_exists('\Swoole\Event', false)) {
+            unset(static::$_availableEventLoops['swoole']);
+        }
+
+        $loop_name = '';
+        foreach (static::$_availableEventLoops as $name=>$class) {
+            if (\extension_loaded($name)) {
+                $loop_name = $name;
+                break;
+            }
+        }
+
+        if ($loop_name) {
+            static::$eventLoopClass = static::$_availableEventLoops[$loop_name];
+        } else {
+            static::$eventLoopClass =  '\Workerman\Events\Select';
+        }
+        return static::$eventLoopClass;
+    }
+
+    /**
+     * Get all pids of worker processes.
+     *
+     * @return array
+     */
+    protected static function getAllWorkerPids()
+    {
+        $pid_array = array();
+        foreach (static::$_pidMap as $worker_pid_array) {
+            foreach ($worker_pid_array as $worker_pid) {
+                $pid_array[$worker_pid] = $worker_pid;
+            }
+        }
+        return $pid_array;
+    }
+
+    /**
+     * Fork some worker processes.
+     *
+     * @return void
+     */
+    protected static function forkWorkers()
+    {
+        if (static::$_OS === \OS_TYPE_LINUX) {
+            static::forkWorkersForLinux();
+        } else {
+            static::forkWorkersForWindows();
+        }
+    }
+
+    /**
+     * Fork some worker processes.
+     *
+     * @return void
+     */
+    protected static function forkWorkersForLinux()
+    {
+
+        foreach (static::$_workers as $worker) {
+            if (static::$_status === static::STATUS_STARTING) {
+                if (empty($worker->name)) {
+                    $worker->name = $worker->getSocketName();
+                }
+                $worker_name_length = \strlen($worker->name);
+                if (static::$_maxWorkerNameLength < $worker_name_length) {
+                    static::$_maxWorkerNameLength = $worker_name_length;
+                }
+            }
+
+            while (\count(static::$_pidMap[$worker->workerId]) < $worker->count) {
+                static::forkOneWorkerForLinux($worker);
+            }
+        }
+    }
+
+    /**
+     * Fork some worker processes.
+     *
+     * @return void
+     */
+    protected static function forkWorkersForWindows()
+    {
+        $files = static::getStartFilesForWindows();
+        global $argv;
+        if(\in_array('-q', $argv) || \count($files) === 1)
+        {
+            if(\count(static::$_workers) > 1)
+            {
+                static::safeEcho("@@@ Error: multi workers init in one php file are not support @@@\r\n");
+                static::safeEcho("@@@ See http://doc.workerman.net/faq/multi-woker-for-windows.html @@@\r\n");
+            }
+            elseif(\count(static::$_workers) <= 0)
+            {
+                exit("@@@no worker inited@@@\r\n\r\n");
+            }
+
+            \reset(static::$_workers);
+            /** @var Worker $worker */
+            $worker = current(static::$_workers);
+
+            // Display UI.
+            static::safeEcho(\str_pad($worker->name, 30) . \str_pad($worker->getSocketName(), 36) . \str_pad($worker->count, 10) . "[ok]\n");
+            $worker->listen();
+            $worker->run();
+            exit("@@@child exit@@@\r\n");
+        }
+        else
+        {
+            static::$globalEvent = new \Workerman\Events\Select();
+            Timer::init(static::$globalEvent);
+            foreach($files as $start_file)
+            {
+                static::forkOneWorkerForWindows($start_file);
+            }
+        }
+    }
+
+    /**
+     * Get start files for windows.
+     *
+     * @return array
+     */
+    public static function getStartFilesForWindows() {
+        global $argv;
+        $files = array();
+        foreach($argv as $file)
+        {
+            if(\is_file($file))
+            {
+                $files[$file] = $file;
+            }
+        }
+        return $files;
+    }
+
+    /**
+     * Fork one worker process.
+     *
+     * @param string $start_file
+     */
+    public static function forkOneWorkerForWindows($start_file)
+    {
+        $start_file = \realpath($start_file);
+
+        $descriptorspec = array(
+            STDIN, STDOUT, STDOUT
+        );
+
+        $pipes       = array();
+        $process     = \proc_open("php \"$start_file\" -q", $descriptorspec, $pipes);
+
+        if (empty(static::$globalEvent)) {
+            static::$globalEvent = new Select();
+            Timer::init(static::$globalEvent);
+        }
+
+        // 保存子进程句柄
+        static::$_processForWindows[$start_file] = array($process, $start_file);
+    }
+
+    /**
+     * check worker status for windows.
+     * @return void
+     */
+    public static function checkWorkerStatusForWindows()
+    {
+        foreach(static::$_processForWindows as $process_data)
+        {
+            $process = $process_data[0];
+            $start_file = $process_data[1];
+            $status = \proc_get_status($process);
+            if(isset($status['running']))
+            {
+                if(!$status['running'])
+                {
+                    static::safeEcho("process $start_file terminated and try to restart\n");
+                    \proc_close($process);
+                    static::forkOneWorkerForWindows($start_file);
+                }
+            }
+            else
+            {
+                static::safeEcho("proc_get_status fail\n");
+            }
+        }
+    }
+
+
+    /**
+     * Fork one worker process.
+     *
+     * @param self $worker
+     * @throws Exception
+     */
+    protected static function forkOneWorkerForLinux(self $worker)
+    {
+        // Get available worker id.
+        $id = static::getId($worker->workerId, 0);
+        if ($id === false) {
+            return;
+        }
+        $pid = \pcntl_fork();
+        // For master process.
+        if ($pid > 0) {
+            static::$_pidMap[$worker->workerId][$pid] = $pid;
+            static::$_idMap[$worker->workerId][$id]   = $pid;
+        } // For child processes.
+        elseif (0 === $pid) {
+            \srand();
+            \mt_srand();
+            if ($worker->reusePort) {
+                $worker->listen();
+            }
+            if (static::$_status === static::STATUS_STARTING) {
+                static::resetStd();
+            }
+            static::$_pidMap  = array();
+            // Remove other listener.
+            foreach(static::$_workers as $key => $one_worker) {
+                if ($one_worker->workerId !== $worker->workerId) {
+                    $one_worker->unlisten();
+                    unset(static::$_workers[$key]);
+                }
+            }
+            Timer::delAll();
+            static::setProcessTitle(self::$processTitle . ': worker process  ' . $worker->name . ' ' . $worker->getSocketName());
+            $worker->setUserAndGroup();
+            $worker->id = $id;
+            $worker->run();
+            if (strpos(static::$eventLoopClass, 'Workerman\Events\Swoole') !== false) {
+                exit(0);
+            }
+            $err = new Exception('event-loop exited');
+            static::log($err);
+            exit(250);
+        } else {
+            throw new Exception("forkOneWorker fail");
+        }
+    }
+
+    /**
+     * Get worker id.
+     *
+     * @param int $worker_id
+     * @param int $pid
+     *
+     * @return integer
+     */
+    protected static function getId($worker_id, $pid)
+    {
+        return \array_search($pid, static::$_idMap[$worker_id]);
+    }
+
+    /**
+     * Set unix user and group for current process.
+     *
+     * @return void
+     */
+    public function setUserAndGroup()
+    {
+        // Get uid.
+        $user_info = \posix_getpwnam($this->user);
+        if (!$user_info) {
+            static::log("Warning: User {$this->user} not exsits");
+            return;
+        }
+        $uid = $user_info['uid'];
+        // Get gid.
+        if ($this->group) {
+            $group_info = \posix_getgrnam($this->group);
+            if (!$group_info) {
+                static::log("Warning: Group {$this->group} not exsits");
+                return;
+            }
+            $gid = $group_info['gid'];
+        } else {
+            $gid = $user_info['gid'];
+        }
+
+        // Set uid and gid.
+        if ($uid !== \posix_getuid() || $gid !== \posix_getgid()) {
+            if (!\posix_setgid($gid) || !\posix_initgroups($user_info['name'], $gid) || !\posix_setuid($uid)) {
+                static::log("Warning: change gid or uid fail.");
+            }
+        }
+    }
+
+    /**
+     * Set process name.
+     *
+     * @param string $title
+     * @return void
+     */
+    protected static function setProcessTitle($title)
+    {
+        \set_error_handler(function(){});
+        // >=php 5.5
+        if (\function_exists('cli_set_process_title')) {
+            \cli_set_process_title($title);
+        } // Need proctitle when php<=5.5 .
+        elseif (\extension_loaded('proctitle') && \function_exists('setproctitle')) {
+            \setproctitle($title);
+        }
+        \restore_error_handler();
+    }
+
+    /**
+     * Monitor all child processes.
+     *
+     * @return void
+     */
+    protected static function monitorWorkers()
+    {
+        if (static::$_OS === \OS_TYPE_LINUX) {
+            static::monitorWorkersForLinux();
+        } else {
+            static::monitorWorkersForWindows();
+        }
+    }
+
+    /**
+     * Monitor all child processes.
+     *
+     * @return void
+     */
+    protected static function monitorWorkersForLinux()
+    {
+        static::$_status = static::STATUS_RUNNING;
+        while (1) {
+            // Calls signal handlers for pending signals.
+            \pcntl_signal_dispatch();
+            // Suspends execution of the current process until a child has exited, or until a signal is delivered
+            $status = 0;
+            $pid    = \pcntl_wait($status, \WUNTRACED);
+            // Calls signal handlers for pending signals again.
+            \pcntl_signal_dispatch();
+            // If a child has already exited.
+            if ($pid > 0) {
+                // Find out which worker process exited.
+                foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+                    if (isset($worker_pid_array[$pid])) {
+                        $worker = static::$_workers[$worker_id];
+                        // Exit status.
+                        if ($status !== 0) {
+                            static::log("worker[" . $worker->name . ":$pid] exit with status $status");
+                        }
+
+                        // For Statistics.
+                        if (!isset(static::$_globalStatistics['worker_exit_info'][$worker_id][$status])) {
+                            static::$_globalStatistics['worker_exit_info'][$worker_id][$status] = 0;
+                        }
+                        ++static::$_globalStatistics['worker_exit_info'][$worker_id][$status];
+
+                        // Clear process data.
+                        unset(static::$_pidMap[$worker_id][$pid]);
+
+                        // Mark id is available.
+                        $id                              = static::getId($worker_id, $pid);
+                        static::$_idMap[$worker_id][$id] = 0;
+
+                        break;
+                    }
+                }
+                // Is still running state then fork a new worker process.
+                if (static::$_status !== static::STATUS_SHUTDOWN) {
+                    static::forkWorkers();
+                    // If reloading continue.
+                    if (isset(static::$_pidsToRestart[$pid])) {
+                        unset(static::$_pidsToRestart[$pid]);
+                        static::reload();
+                    }
+                }
+            }
+
+            // If shutdown state and all child processes exited then master process exit.
+            if (static::$_status === static::STATUS_SHUTDOWN && !static::getAllWorkerPids()) {
+                static::exitAndClearAll();
+            }
+        }
+    }
+
+    /**
+     * Monitor all child processes.
+     *
+     * @return void
+     */
+    protected static function monitorWorkersForWindows()
+    {
+        Timer::add(1, "\\Workerman\\Worker::checkWorkerStatusForWindows");
+
+        static::$globalEvent->loop();
+    }
+
+    /**
+     * Exit current process.
+     *
+     * @return void
+     */
+    protected static function exitAndClearAll()
+    {
+        foreach (static::$_workers as $worker) {
+            $socket_name = $worker->getSocketName();
+            if ($worker->transport === 'unix' && $socket_name) {
+                list(, $address) = \explode(':', $socket_name, 2);
+                $address = substr($address, strpos($address, '/') + 2);
+                @\unlink($address);
+            }
+        }
+        @\unlink(static::$pidFile);
+        static::log("Workerman[" . \basename(static::$_startFile) . "] has been stopped");
+        if (static::$onMasterStop) {
+            \call_user_func(static::$onMasterStop);
+        }
+        exit(0);
+    }
+
+    /**
+     * Execute reload.
+     *
+     * @return void
+     */
+    protected static function reload()
+    {
+        // For master process.
+        if (static::$_masterPid === \posix_getpid()) {
+            // Set reloading state.
+            if (static::$_status !== static::STATUS_RELOADING && static::$_status !== static::STATUS_SHUTDOWN) {
+                static::log("Workerman[" . \basename(static::$_startFile) . "] reloading");
+                static::$_status = static::STATUS_RELOADING;
+                // Try to emit onMasterReload callback.
+                if (static::$onMasterReload) {
+                    try {
+                        \call_user_func(static::$onMasterReload);
+                    } catch (\Exception $e) {
+                        static::stopAll(250, $e);
+                    } catch (\Error $e) {
+                        static::stopAll(250, $e);
+                    }
+                    static::initId();
+                }
+            }
+
+            if (static::$_gracefulStop) {
+                $sig = \SIGQUIT;
+            } else {
+                $sig = \SIGUSR1;
+            }
+
+            // Send reload signal to all child processes.
+            $reloadable_pid_array = array();
+            foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+                $worker = static::$_workers[$worker_id];
+                if ($worker->reloadable) {
+                    foreach ($worker_pid_array as $pid) {
+                        $reloadable_pid_array[$pid] = $pid;
+                    }
+                } else {
+                    foreach ($worker_pid_array as $pid) {
+                        // Send reload signal to a worker process which reloadable is false.
+                        \posix_kill($pid, $sig);
+                    }
+                }
+            }
+
+            // Get all pids that are waiting reload.
+            static::$_pidsToRestart = \array_intersect(static::$_pidsToRestart, $reloadable_pid_array);
+
+            // Reload complete.
+            if (empty(static::$_pidsToRestart)) {
+                if (static::$_status !== static::STATUS_SHUTDOWN) {
+                    static::$_status = static::STATUS_RUNNING;
+                }
+                return;
+            }
+            // Continue reload.
+            $one_worker_pid = \current(static::$_pidsToRestart);
+            // Send reload signal to a worker process.
+            \posix_kill($one_worker_pid, $sig);
+            // If the process does not exit after static::KILL_WORKER_TIMER_TIME seconds try to kill it.
+            if(!static::$_gracefulStop){
+                Timer::add(static::KILL_WORKER_TIMER_TIME, '\posix_kill', array($one_worker_pid, \SIGKILL), false);
+            }
+        } // For child processes.
+        else {
+            \reset(static::$_workers);
+            $worker = \current(static::$_workers);
+            // Try to emit onWorkerReload callback.
+            if ($worker->onWorkerReload) {
+                try {
+                    \call_user_func($worker->onWorkerReload, $worker);
+                } catch (\Exception $e) {
+                    static::stopAll(250, $e);
+                } catch (\Error $e) {
+                    static::stopAll(250, $e);
+                }
+            }
+
+            if ($worker->reloadable) {
+                static::stopAll();
+            }
+        }
+    }
+
+    /**
+     * Stop all.
+     *
+     * @param int $code
+     * @param string $log
+     */
+    public static function stopAll($code = 0, $log = '')
+    {
+        if ($log) {
+            static::log($log);
+        }
+
+        static::$_status = static::STATUS_SHUTDOWN;
+        // For master process.
+        if (\DIRECTORY_SEPARATOR === '/' && static::$_masterPid === \posix_getpid()) {
+            static::log("Workerman[" . \basename(static::$_startFile) . "] stopping ...");
+            $worker_pid_array = static::getAllWorkerPids();
+            // Send stop signal to all child processes.
+            if (static::$_gracefulStop) {
+                $sig = \SIGHUP;
+            } else {
+                $sig = \SIGINT;
+            }
+            foreach ($worker_pid_array as $worker_pid) {
+                \posix_kill($worker_pid, $sig);
+                if(!static::$_gracefulStop){
+                    Timer::add(static::KILL_WORKER_TIMER_TIME, '\posix_kill', array($worker_pid, \SIGKILL), false);
+                }
+            }
+            Timer::add(1, "\\Workerman\\Worker::checkIfChildRunning");
+            // Remove statistics file.
+            if (\is_file(static::$_statisticsFile)) {
+                @\unlink(static::$_statisticsFile);
+            }
+        } // For child processes.
+        else {
+            // Execute exit.
+            foreach (static::$_workers as $worker) {
+                if(!$worker->stopping){
+                    $worker->stop();
+                    $worker->stopping = true;
+                }
+            }
+            if (!static::$_gracefulStop || ConnectionInterface::$statistics['connection_count'] <= 0) {
+                static::$_workers = array();
+                if (static::$globalEvent) {
+                    static::$globalEvent->destroy();
+                }
+
+                try {
+                    exit($code);
+                } catch (Exception $e) {
+
+                }
+            }
+        }
+    }
+
+    /**
+     * check if child processes is really running
+     */
+    public static function checkIfChildRunning()
+    {
+        foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+            foreach ($worker_pid_array as $pid => $worker_pid) {
+                if (!\posix_kill($pid, 0)) {
+                    unset(static::$_pidMap[$worker_id][$pid]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Get process status.
+     *
+     * @return number
+     */
+    public static function getStatus()
+    {
+        return static::$_status;
+    }
+
+    /**
+     * If stop gracefully.
+     *
+     * @return bool
+     */
+    public static function getGracefulStop()
+    {
+        return static::$_gracefulStop;
+    }
+
+    /**
+     * Write statistics data to disk.
+     *
+     * @return void
+     */
+    protected static function writeStatisticsToStatusFile()
+    {
+        // For master process.
+        if (static::$_masterPid === \posix_getpid()) {
+            $all_worker_info = array();
+            foreach(static::$_pidMap as $worker_id => $pid_array) {
+                /** @var /Workerman/Worker $worker */
+                $worker = static::$_workers[$worker_id];
+                foreach($pid_array as $pid) {
+                    $all_worker_info[$pid] = array('name' => $worker->name, 'listen' => $worker->getSocketName());
+                }
+            }
+
+            \file_put_contents(static::$_statisticsFile, \serialize($all_worker_info)."\n", \FILE_APPEND);
+            $loadavg = \function_exists('sys_getloadavg') ? \array_map('round', \sys_getloadavg(), array(2,2,2)) : array('-', '-', '-');
+            \file_put_contents(static::$_statisticsFile,
+                "----------------------------------------------GLOBAL STATUS----------------------------------------------------\n", \FILE_APPEND);
+            \file_put_contents(static::$_statisticsFile,
+                'Workerman version:' . static::VERSION . "          PHP version:" . \PHP_VERSION . "\n", \FILE_APPEND);
+            \file_put_contents(static::$_statisticsFile, 'start time:' . \date('Y-m-d H:i:s',
+                    static::$_globalStatistics['start_timestamp']) . '   run ' . \floor((\time() - static::$_globalStatistics['start_timestamp']) / (24 * 60 * 60)) . ' days ' . \floor(((\time() - static::$_globalStatistics['start_timestamp']) % (24 * 60 * 60)) / (60 * 60)) . " hours   \n",
+                FILE_APPEND);
+            $load_str = 'load average: ' . \implode(", ", $loadavg);
+            \file_put_contents(static::$_statisticsFile,
+                \str_pad($load_str, 33) . 'event-loop:' . static::getEventLoopName() . "\n", \FILE_APPEND);
+            \file_put_contents(static::$_statisticsFile,
+                \count(static::$_pidMap) . ' workers       ' . \count(static::getAllWorkerPids()) . " processes\n",
+                \FILE_APPEND);
+            \file_put_contents(static::$_statisticsFile,
+                \str_pad('worker_name', static::$_maxWorkerNameLength) . " exit_status      exit_count\n", \FILE_APPEND);
+            foreach (static::$_pidMap as $worker_id => $worker_pid_array) {
+                $worker = static::$_workers[$worker_id];
+                if (isset(static::$_globalStatistics['worker_exit_info'][$worker_id])) {
+                    foreach (static::$_globalStatistics['worker_exit_info'][$worker_id] as $worker_exit_status => $worker_exit_count) {
+                        \file_put_contents(static::$_statisticsFile,
+                            \str_pad($worker->name, static::$_maxWorkerNameLength) . " " . \str_pad($worker_exit_status,
+                                16) . " $worker_exit_count\n", \FILE_APPEND);
+                    }
+                } else {
+                    \file_put_contents(static::$_statisticsFile,
+                        \str_pad($worker->name, static::$_maxWorkerNameLength) . " " . \str_pad(0, 16) . " 0\n",
+                        \FILE_APPEND);
+                }
+            }
+            \file_put_contents(static::$_statisticsFile,
+                "----------------------------------------------PROCESS STATUS---------------------------------------------------\n",
+                \FILE_APPEND);
+            \file_put_contents(static::$_statisticsFile,
+                "pid\tmemory  " . \str_pad('listening', static::$_maxSocketNameLength) . " " . \str_pad('worker_name',
+                    static::$_maxWorkerNameLength) . " connections " . \str_pad('send_fail', 9) . " "
+                . \str_pad('timers', 8) . \str_pad('total_request', 13) ." qps    status\n", \FILE_APPEND);
+
+            \chmod(static::$_statisticsFile, 0722);
+
+            foreach (static::getAllWorkerPids() as $worker_pid) {
+                \posix_kill($worker_pid, \SIGUSR2);
+            }
+            return;
+        }
+
+        // For child processes.
+        \reset(static::$_workers);
+        /** @var \Workerman\Worker $worker */
+        $worker            = current(static::$_workers);
+        $worker_status_str = \posix_getpid() . "\t" . \str_pad(round(memory_get_usage(true) / (1024 * 1024), 2) . "M", 7)
+            . " " . \str_pad($worker->getSocketName(), static::$_maxSocketNameLength) . " "
+            . \str_pad(($worker->name === $worker->getSocketName() ? 'none' : $worker->name), static::$_maxWorkerNameLength)
+            . " ";
+        $worker_status_str .= \str_pad(ConnectionInterface::$statistics['connection_count'], 11)
+            . " " .  \str_pad(ConnectionInterface::$statistics['send_fail'], 9)
+            . " " . \str_pad(static::$globalEvent->getTimerCount(), 7)
+            . " " . \str_pad(ConnectionInterface::$statistics['total_request'], 13) . "\n";
+        \file_put_contents(static::$_statisticsFile, $worker_status_str, \FILE_APPEND);
+    }
+
+    /**
+     * Write statistics data to disk.
+     *
+     * @return void
+     */
+    protected static function writeConnectionsStatisticsToStatusFile()
+    {
+        // For master process.
+        if (static::$_masterPid === \posix_getpid()) {
+            \file_put_contents(static::$_statisticsFile, "--------------------------------------------------------------------- WORKERMAN CONNECTION STATUS --------------------------------------------------------------------------------\n", \FILE_APPEND);
+            \file_put_contents(static::$_statisticsFile, "PID      Worker          CID       Trans   Protocol        ipv4   ipv6   Recv-Q       Send-Q       Bytes-R      Bytes-W       Status         Local Address          Foreign Address\n", \FILE_APPEND);
+            \chmod(static::$_statisticsFile, 0722);
+            foreach (static::getAllWorkerPids() as $worker_pid) {
+                \posix_kill($worker_pid, \SIGIO);
+            }
+            return;
+        }
+
+        // For child processes.
+        $bytes_format = function($bytes)
+        {
+            if($bytes > 1024*1024*1024*1024) {
+                return round($bytes/(1024*1024*1024*1024), 1)."TB";
+            }
+            if($bytes > 1024*1024*1024) {
+                return round($bytes/(1024*1024*1024), 1)."GB";
+            }
+            if($bytes > 1024*1024) {
+                return round($bytes/(1024*1024), 1)."MB";
+            }
+            if($bytes > 1024) {
+                return round($bytes/(1024), 1)."KB";
+            }
+            return $bytes."B";
+        };
+
+        $pid = \posix_getpid();
+        $str = '';
+        \reset(static::$_workers);
+        $current_worker = current(static::$_workers);
+        $default_worker_name = $current_worker->name;
+
+        /** @var \Workerman\Worker $worker */
+        foreach(TcpConnection::$connections as $connection) {
+            /** @var \Workerman\Connection\TcpConnection $connection */
+            $transport      = $connection->transport;
+            $ipv4           = $connection->isIpV4() ? ' 1' : ' 0';
+            $ipv6           = $connection->isIpV6() ? ' 1' : ' 0';
+            $recv_q         = $bytes_format($connection->getRecvBufferQueueSize());
+            $send_q         = $bytes_format($connection->getSendBufferQueueSize());
+            $local_address  = \trim($connection->getLocalAddress());
+            $remote_address = \trim($connection->getRemoteAddress());
+            $state          = $connection->getStatus(false);
+            $bytes_read     = $bytes_format($connection->bytesRead);
+            $bytes_written  = $bytes_format($connection->bytesWritten);
+            $id             = $connection->id;
+            $protocol       = $connection->protocol ? $connection->protocol : $connection->transport;
+            $pos            = \strrpos($protocol, '\\');
+            if ($pos) {
+                $protocol = \substr($protocol, $pos+1);
+            }
+            if (\strlen($protocol) > 15) {
+                $protocol = \substr($protocol, 0, 13) . '..';
+            }
+            $worker_name = isset($connection->worker) ? $connection->worker->name : $default_worker_name;
+            if (\strlen($worker_name) > 14) {
+                $worker_name = \substr($worker_name, 0, 12) . '..';
+            }
+            $str .= \str_pad($pid, 9) . \str_pad($worker_name, 16) .  \str_pad($id, 10) . \str_pad($transport, 8)
+                . \str_pad($protocol, 16) . \str_pad($ipv4, 7) . \str_pad($ipv6, 7) . \str_pad($recv_q, 13)
+                . \str_pad($send_q, 13) . \str_pad($bytes_read, 13) . \str_pad($bytes_written, 13) . ' '
+                . \str_pad($state, 14) . ' ' . \str_pad($local_address, 22) . ' ' . \str_pad($remote_address, 22) ."\n";
+        }
+        if ($str) {
+            \file_put_contents(static::$_statisticsFile, $str, \FILE_APPEND);
+        }
+    }
+
+    /**
+     * Check errors when current process exited.
+     *
+     * @return void
+     */
+    public static function checkErrors()
+    {
+        if (static::STATUS_SHUTDOWN !== static::$_status) {
+            $error_msg = static::$_OS === \OS_TYPE_LINUX ? 'Worker['. \posix_getpid() .'] process terminated' : 'Worker process terminated';
+            $errors    = error_get_last();
+            if ($errors && ($errors['type'] === \E_ERROR ||
+                    $errors['type'] === \E_PARSE ||
+                    $errors['type'] === \E_CORE_ERROR ||
+                    $errors['type'] === \E_COMPILE_ERROR ||
+                    $errors['type'] === \E_RECOVERABLE_ERROR)
+            ) {
+                $error_msg .= ' with ERROR: ' . static::getErrorType($errors['type']) . " \"{$errors['message']} in {$errors['file']} on line {$errors['line']}\"";
+            }
+            static::log($error_msg);
+        }
+    }
+
+    /**
+     * Get error message by error code.
+     *
+     * @param integer $type
+     * @return string
+     */
+    protected static function getErrorType($type)
+    {
+        if(isset(self::$_errorType[$type])) {
+            return self::$_errorType[$type];
+        }
+
+        return '';
+    }
+
+    /**
+     * Log.
+     *
+     * @param string $msg
+     * @return void
+     */
+    public static function log($msg)
+    {
+        $msg = $msg . "\n";
+        if (!static::$daemonize) {
+            static::safeEcho($msg);
+        }
+        \file_put_contents((string)static::$logFile, \date('Y-m-d H:i:s') . ' ' . 'pid:'
+            . (static::$_OS === \OS_TYPE_LINUX ? \posix_getpid() : 1) . ' ' . $msg, \FILE_APPEND | \LOCK_EX);
+    }
+
+    /**
+     * Safe Echo.
+     * @param string $msg
+     * @param bool   $decorated
+     * @return bool
+     */
+    public static function safeEcho($msg, $decorated = false)
+    {
+        $stream = static::outputStream();
+        if (!$stream) {
+            return false;
+        }
+        if (!$decorated) {
+            $line = $white = $green = $end = '';
+            if (static::$_outputDecorated) {
+                $line = "\033[1A\n\033[K";
+                $white = "\033[47;30m";
+                $green = "\033[32;40m";
+                $end = "\033[0m";
+            }
+            $msg = \str_replace(array('<n>', '<w>', '<g>'), array($line, $white, $green), $msg);
+            $msg = \str_replace(array('</n>', '</w>', '</g>'), $end, $msg);
+        } elseif (!static::$_outputDecorated) {
+            return false;
+        }
+        \fwrite($stream, $msg);
+        \fflush($stream);
+        return true;
+    }
+
+    /**
+     * @param resource|null $stream
+     * @return bool|resource
+     */
+    private static function outputStream($stream = null)
+    {
+        if (!$stream) {
+            $stream = static::$_outputStream ? static::$_outputStream : \STDOUT;
+        }
+        if (!$stream || !\is_resource($stream) || 'stream' !== \get_resource_type($stream)) {
+            return false;
+        }
+        $stat = \fstat($stream);
+        if (!$stat) {
+            return false;
+        }
+        if (($stat['mode'] & 0170000) === 0100000) {
+            // file
+            static::$_outputDecorated = false;
+        } else {
+            static::$_outputDecorated =
+                static::$_OS === \OS_TYPE_LINUX &&
+                \function_exists('posix_isatty') &&
+                \posix_isatty($stream);
+        }
+        return static::$_outputStream = $stream;
+    }
+
+    /**
+     * Construct.
+     *
+     * @param string $socket_name
+     * @param array  $context_option
+     */
+    public function __construct($socket_name = '', array $context_option = array())
+    {
+        // Save all worker instances.
+        $this->workerId                    = \spl_object_hash($this);
+        static::$_workers[$this->workerId] = $this;
+        static::$_pidMap[$this->workerId]  = array();
+
+        // Get autoload root path.
+        $backtrace               = \debug_backtrace();
+        $this->_autoloadRootPath = \dirname($backtrace[0]['file']);
+        Autoloader::setRootPath($this->_autoloadRootPath);
+
+        // Context for socket.
+        if ($socket_name) {
+            $this->_socketName = $socket_name;
+            if (!isset($context_option['socket']['backlog'])) {
+                $context_option['socket']['backlog'] = static::DEFAULT_BACKLOG;
+            }
+            $this->_context = \stream_context_create($context_option);
+        }
+
+        // Turn reusePort on.
+        /*if (static::$_OS === \OS_TYPE_LINUX  // if linux
+            && \version_compare(\PHP_VERSION,'7.0.0', 'ge') // if php >= 7.0.0
+            && \version_compare(php_uname('r'), '3.9', 'ge') // if kernel >=3.9
+            && \strtolower(\php_uname('s')) !== 'darwin' // if not Mac OS
+            && strpos($socket_name,'unix') !== 0) { // if not unix socket
+
+            $this->reusePort = true;
+        }*/
+    }
+
+
+    /**
+     * Listen.
+     *
+     * @throws Exception
+     */
+    public function listen()
+    {
+        if (!$this->_socketName) {
+            return;
+        }
+
+        // Autoload.
+        Autoloader::setRootPath($this->_autoloadRootPath);
+
+        if (!$this->_mainSocket) {
+
+            $local_socket = $this->parseSocketAddress();
+
+            // Flag.
+            $flags = $this->transport === 'udp' ? \STREAM_SERVER_BIND : \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN;
+            $errno = 0;
+            $errmsg = '';
+            // SO_REUSEPORT.
+            if ($this->reusePort) {
+                \stream_context_set_option($this->_context, 'socket', 'so_reuseport', 1);
+            }
+
+            // Create an Internet or Unix domain server socket.
+            $this->_mainSocket = \stream_socket_server($local_socket, $errno, $errmsg, $flags, $this->_context);
+            if (!$this->_mainSocket) {
+                throw new Exception($errmsg);
+            }
+
+            if ($this->transport === 'ssl') {
+                \stream_socket_enable_crypto($this->_mainSocket, false);
+            } elseif ($this->transport === 'unix') {
+                $socket_file = \substr($local_socket, 7);
+                if ($this->user) {
+                    \chown($socket_file, $this->user);
+                }
+                if ($this->group) {
+                    \chgrp($socket_file, $this->group);
+                }
+            }
+
+            // Try to open keepalive for tcp and disable Nagle algorithm.
+            if (\function_exists('socket_import_stream') && static::$_builtinTransports[$this->transport] === 'tcp') {
+                \set_error_handler(function(){});
+                $socket = \socket_import_stream($this->_mainSocket);
+                \socket_set_option($socket, \SOL_SOCKET, \SO_KEEPALIVE, 1);
+                \socket_set_option($socket, \SOL_TCP, \TCP_NODELAY, 1);
+                \restore_error_handler();
+            }
+
+            // Non blocking.
+            \stream_set_blocking($this->_mainSocket, false);
+        }
+
+        $this->resumeAccept();
+    }
+
+    /**
+     * Unlisten.
+     *
+     * @return void
+     */
+    public function unlisten() {
+        $this->pauseAccept();
+        if ($this->_mainSocket) {
+            \set_error_handler(function(){});
+            \fclose($this->_mainSocket);
+            \restore_error_handler();
+            $this->_mainSocket = null;
+        }
+    }
+
+    /**
+     * Parse local socket address.
+     *
+     * @throws Exception
+     */
+    protected function parseSocketAddress() {
+        if (!$this->_socketName) {
+            return;
+        }
+        // Get the application layer communication protocol and listening address.
+        list($scheme, $address) = \explode(':', $this->_socketName, 2);
+        // Check application layer protocol class.
+        if (!isset(static::$_builtinTransports[$scheme])) {
+            $scheme         = \ucfirst($scheme);
+            $this->protocol = \substr($scheme,0,1)==='\\' ? $scheme : 'Protocols\\' . $scheme;
+            if (!\class_exists($this->protocol)) {
+                $this->protocol = "Workerman\\Protocols\\$scheme";
+                if (!\class_exists($this->protocol)) {
+                    throw new Exception("class \\Protocols\\$scheme not exist");
+                }
+            }
+
+            if (!isset(static::$_builtinTransports[$this->transport])) {
+                throw new Exception('Bad worker->transport ' . \var_export($this->transport, true));
+            }
+        } else {
+            $this->transport = $scheme;
+        }
+        //local socket
+        return static::$_builtinTransports[$this->transport] . ":" . $address;
+    }
+
+    /**
+     * Pause accept new connections.
+     *
+     * @return void
+     */
+    public function pauseAccept()
+    {
+        if (static::$globalEvent && false === $this->_pauseAccept && $this->_mainSocket) {
+            static::$globalEvent->del($this->_mainSocket, EventInterface::EV_READ);
+            $this->_pauseAccept = true;
+        }
+    }
+
+    /**
+     * Resume accept new connections.
+     *
+     * @return void
+     */
+    public function resumeAccept()
+    {
+        // Register a listener to be notified when server socket is ready to read.
+        if (static::$globalEvent && true === $this->_pauseAccept && $this->_mainSocket) {
+            if ($this->transport !== 'udp') {
+                static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection'));
+            } else {
+                static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptUdpConnection'));
+            }
+            $this->_pauseAccept = false;
+        }
+    }
+
+    /**
+     * Get socket name.
+     *
+     * @return string
+     */
+    public function getSocketName()
+    {
+        return $this->_socketName ? \lcfirst($this->_socketName) : 'none';
+    }
+
+    /**
+     * Run worker instance.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //Update process state.
+        static::$_status = static::STATUS_RUNNING;
+
+        // Register shutdown function for checking errors.
+        \register_shutdown_function(array("\\Workerman\\Worker", 'checkErrors'));
+
+        // Set autoload root path.
+        Autoloader::setRootPath($this->_autoloadRootPath);
+
+        // Create a global event loop.
+        if (!static::$globalEvent) {
+            $event_loop_class = static::getEventLoopName();
+            static::$globalEvent = new $event_loop_class;
+            $this->resumeAccept();
+        }
+
+        // Reinstall signal.
+        static::reinstallSignal();
+
+        // Init Timer.
+        Timer::init(static::$globalEvent);
+
+        // Set an empty onMessage callback.
+        if (empty($this->onMessage)) {
+            $this->onMessage = function () {};
+        }
+
+        \restore_error_handler();
+
+        // Try to emit onWorkerStart callback.
+        if ($this->onWorkerStart) {
+            try {
+                \call_user_func($this->onWorkerStart, $this);
+            } catch (\Exception $e) {
+                // Avoid rapid infinite loop exit.
+                sleep(1);
+                static::stopAll(250, $e);
+            } catch (\Error $e) {
+                // Avoid rapid infinite loop exit.
+                sleep(1);
+                static::stopAll(250, $e);
+            }
+        }
+
+        // Main loop.
+        static::$globalEvent->loop();
+    }
+
+    /**
+     * Stop current worker instance.
+     *
+     * @return void
+     */
+    public function stop()
+    {
+        // Try to emit onWorkerStop callback.
+        if ($this->onWorkerStop) {
+            try {
+                \call_user_func($this->onWorkerStop, $this);
+            } catch (\Exception $e) {
+                static::stopAll(250, $e);
+            } catch (\Error $e) {
+                static::stopAll(250, $e);
+            }
+        }
+        // Remove listener for server socket.
+        $this->unlisten();
+        // Close all connections for the worker.
+        if (!static::$_gracefulStop) {
+            foreach ($this->connections as $connection) {
+                $connection->close();
+            }
+        }
+        // Clear callback.
+        $this->onMessage = $this->onClose = $this->onError = $this->onBufferDrain = $this->onBufferFull = null;
+    }
+
+    /**
+     * Accept a connection.
+     *
+     * @param resource $socket
+     * @return void
+     */
+    public function acceptConnection($socket)
+    {
+        // Accept a connection on server socket.
+        \set_error_handler(function(){});
+        $new_socket = \stream_socket_accept($socket, 0, $remote_address);
+        \restore_error_handler();
+
+        // Thundering herd.
+        if (!$new_socket) {
+            return;
+        }
+
+        // TcpConnection.
+        $connection                         = new TcpConnection($new_socket, $remote_address);
+        $this->connections[$connection->id] = $connection;
+        $connection->worker                 = $this;
+        $connection->protocol               = $this->protocol;
+        $connection->transport              = $this->transport;
+        $connection->onMessage              = $this->onMessage;
+        $connection->onClose                = $this->onClose;
+        $connection->onError                = $this->onError;
+        $connection->onBufferDrain          = $this->onBufferDrain;
+        $connection->onBufferFull           = $this->onBufferFull;
+
+        // Try to emit onConnect callback.
+        if ($this->onConnect) {
+            try {
+                \call_user_func($this->onConnect, $connection);
+            } catch (\Exception $e) {
+                static::stopAll(250, $e);
+            } catch (\Error $e) {
+                static::stopAll(250, $e);
+            }
+        }
+    }
+
+    /**
+     * For udp package.
+     *
+     * @param resource $socket
+     * @return bool
+     */
+    public function acceptUdpConnection($socket)
+    {
+        \set_error_handler(function(){});
+        $recv_buffer = \stream_socket_recvfrom($socket, static::MAX_UDP_PACKAGE_SIZE, 0, $remote_address);
+        \restore_error_handler();
+        if (false === $recv_buffer || empty($remote_address)) {
+            return false;
+        }
+        // UdpConnection.
+        $connection           = new UdpConnection($socket, $remote_address);
+        $connection->protocol = $this->protocol;
+        if ($this->onMessage) {
+            try {
+                if ($this->protocol !== null) {
+                    /** @var \Workerman\Protocols\ProtocolInterface $parser */
+                    $parser = $this->protocol;
+                    if ($parser && \method_exists($parser, 'input')) {
+                        while ($recv_buffer !== '') {
+                            $len = $parser::input($recv_buffer, $connection);
+                            if ($len === 0)
+                                return true;
+                            $package = \substr($recv_buffer, 0, $len);
+                            $recv_buffer = \substr($recv_buffer, $len);
+                            $data = $parser::decode($package, $connection);
+                            if ($data === false)
+                                continue;
+                            \call_user_func($this->onMessage, $connection, $data);
+                        }
+                    } else {
+                        $data = $parser::decode($recv_buffer, $connection);
+                        // Discard bad packets.
+                        if ($data === false)
+                            return true;
+                        \call_user_func($this->onMessage, $connection, $data);
+                    }
+                } else {
+                    \call_user_func($this->onMessage, $connection, $recv_buffer);
+                }
+                ++ConnectionInterface::$statistics['total_request'];
+            } catch (\Exception $e) {
+                static::stopAll(250, $e);
+            } catch (\Error $e) {
+                static::stopAll(250, $e);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check master process is alive
+     *
+     * @param int $master_pid
+     * @return bool
+     */
+    protected static function checkMasterIsAlive($master_pid)
+    {
+        if (empty($master_pid)) {
+            return false;
+        }
+
+        $master_is_alive = $master_pid && \posix_kill((int) $master_pid, 0) && \posix_getpid() !== $master_pid;
+        if (!$master_is_alive) {
+            return false;
+        }
+
+        $cmdline = "/proc/{$master_pid}/cmdline";
+        if (!is_readable($cmdline) || empty(static::$processTitle)) {
+            return true;
+        }
+
+        $content = file_get_contents($cmdline);
+        if (empty($content)) {
+            return true;
+        }
+
+        return stripos($content, static::$processTitle) !== false || stripos($content, 'php') !== false;
+    }
+}
+
diff --git a/vendor/workerman/workerman/composer.json b/vendor/workerman/workerman/composer.json
new file mode 100644
index 0000000..fdd4808
--- /dev/null
+++ b/vendor/workerman/workerman/composer.json
@@ -0,0 +1,38 @@
+{
+    "name": "workerman/workerman",
+    "type": "library",
+    "keywords": [
+        "event-loop",
+        "asynchronous"
+    ],
+    "homepage": "http://www.workerman.net",
+    "license": "MIT",
+    "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
+    "authors": [
+        {
+            "name": "walkor",
+            "email": "walkor@workerman.net",
+            "homepage": "http://www.workerman.net",
+            "role": "Developer"
+        }
+    ],
+    "support": {
+        "email": "walkor@workerman.net",
+        "issues": "https://github.com/walkor/workerman/issues",
+        "forum": "http://wenda.workerman.net/",
+        "wiki": "http://doc.workerman.net/",
+        "source": "https://github.com/walkor/workerman"
+    },
+    "require": {
+        "php": ">=5.3"
+    },
+    "suggest": {
+        "ext-event": "For better performance. "
+    },
+    "autoload": {
+        "psr-4": {
+            "Workerman\\": "./"
+        }
+    },
+    "minimum-stability": "dev"
+}
--
libgit2 0.24.0