diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 2ad58e518a..58c262d19b 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1,40 +1,40 @@ -** PLEASE ONLY USE THIS ISSUE TRACKER TO SUBMIT ISSUES WITH THE EXAMPLE VOTING APP ** - -* If you have a bug working with Docker itself, not related to these labs, please file the bug on the [Docker repo](https://github.com/docker/docker) * -* If you would like general support figuring out how to do something with Docker, please use the Docker Slack channel. If you're not on that channel, sign up for the [Docker Community](http://dockr.ly/MeetUp) and you'll get an invite. * -* Or go to the [Docker Forums](https://forums.docker.com/) * - -Please provide the following information so we can assess the issue you're having - -**Description** - - - -**Steps to reproduce the issue, if relevant:** -1. -2. -3. - -**Describe the results you received:** - - -**Describe the results you expected:** - - -**Additional information you deem important (e.g. issue happens only occasionally):** - -**Output of `docker version`:** - -``` -(paste your output here) -``` - -**Output of `docker info`:** - -``` -(paste your output here) -``` - -**Additional environment details (AWS, Docker for Mac, Docker for Windows, VirtualBox, physical, etc.):** +** PLEASE ONLY USE THIS ISSUE TRACKER TO SUBMIT ISSUES WITH THE EXAMPLE VOTING APP ** + +* If you have a bug working with Docker itself, not related to these labs, please file the bug on the [Docker repo](https://github.com/docker/docker) * +* If you would like general support figuring out how to do something with Docker, please use the Docker Slack channel. If you're not on that channel, sign up for the [Docker Community](http://dockr.ly/MeetUp) and you'll get an invite. * +* Or go to the [Docker Forums](https://forums.docker.com/) * + +Please provide the following information so we can assess the issue you're having + +**Description** + + + +**Steps to reproduce the issue, if relevant:** +1. +2. +3. + +**Describe the results you received:** + + +**Describe the results you expected:** + + +**Additional information you deem important (e.g. issue happens only occasionally):** + +**Output of `docker version`:** + +``` +(paste your output here) +``` + +**Output of `docker info`:** + +``` +(paste your output here) +``` + +**Additional environment details (AWS, Docker for Mac, Docker for Windows, VirtualBox, physical, etc.):** diff --git a/.gitignore b/.gitignore index 59d72a2a14..eb68f570bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -*.pyc -project.lock.json -bin/ +*.pyc +project.lock.json +bin/ obj/ \ No newline at end of file diff --git a/LICENSE b/LICENSE index 75191a4dc7..d2cd270e6a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,191 +1,191 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2016 Docker, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2016 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MAINTAINERS b/MAINTAINERS index 628d7a8ab4..c3fe3a7b04 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1,5 +1,5 @@ -Aanand Prasad -Ben Firshman -Fernando Mayo -Mano Marks -Maxime Heckel +Aanand Prasad +Ben Firshman +Fernando Mayo +Mano Marks +Maxime Heckel diff --git a/README.md b/README.md index b8ad8a94af..3c1db8e408 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,60 @@ -Example Voting App -========= - -Getting started ---------------- - -Download [Docker](https://www.docker.com/products/overview). If you are on Mac or Windows, [Docker Compose](https://docs.docker.com/compose) will be automatically installed. On Linux, make sure you have the latest version of [Compose](https://docs.docker.com/compose/install/). If you're using [Docker for Windows](https://docs.docker.com/docker-for-windows/) on Windows 10 pro or later, you must also [switch to Linux containers](https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers). - -Run in this directory: -``` -docker-compose up -``` -The app will be running at [http://localhost:5000](http://localhost:5000), and the results will be at [http://localhost:5001](http://localhost:5001). - -Alternately, if you want to run it on a [Docker Swarm](https://docs.docker.com/engine/swarm/), first make sure you have a swarm. If you don't, run: -``` -docker swarm init -``` -Once you have your swarm, in this directory run: -``` -docker stack deploy --compose-file docker-stack.yml vote -``` - -Run the app in Kubernetes -------------------------- - -The folder k8s-specifications contains the yaml specifications of the Voting App's services. - -Run the following command to create the deployments and services objects: -``` -$ kubectl create -f k8s-specifications/ -deployment "db" created -service "db" created -deployment "redis" created -service "redis" created -deployment "result" created -service "result" created -deployment "vote" created -service "vote" created -deployment "worker" created -``` - -The vote interface is then available on port 31000 on each host of the cluster, the result one is available on port 31001. - -Architecture ------ - -![Architecture diagram](architecture.png) - -* A Python webapp which lets you vote between two options -* A Redis queue which collects new votes -* A .NET worker which consumes votes and stores them in… -* A Postgres database backed by a Docker volume -* A Node.js webapp which shows the results of the voting in real time - - -Note ----- - -The voting application only accepts one vote per client. It does not register votes if a vote has already been submitted from a client. +Example Voting App +========= + +Getting started +--------------- + +Download [Docker](https://www.docker.com/products/overview). If you are on Mac or Windows, [Docker Compose](https://docs.docker.com/compose) will be automatically installed. On Linux, make sure you have the latest version of [Compose](https://docs.docker.com/compose/install/). If you're using [Docker for Windows](https://docs.docker.com/docker-for-windows/) on Windows 10 pro or later, you must also [switch to Linux containers](https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers). + +Run in this directory: +``` +docker-compose up +``` +The app will be running at [http://localhost:5000](http://localhost:5000), and the results will be at [http://localhost:5001](http://localhost:5001). + +Alternately, if you want to run it on a [Docker Swarm](https://docs.docker.com/engine/swarm/), first make sure you have a swarm. If you don't, run: +``` +docker swarm init +``` +Once you have your swarm, in this directory run: +``` +docker stack deploy --compose-file docker-stack.yml vote +``` + +Run the app in Kubernetes +------------------------- + +The folder k8s-specifications contains the yaml specifications of the Voting App's services. + +Run the following command to create the deployments and services objects: +``` +$ kubectl create -f k8s-specifications/ +deployment "db" created +service "db" created +deployment "redis" created +service "redis" created +deployment "result" created +service "result" created +deployment "vote" created +service "vote" created +deployment "worker" created +``` + +The vote interface is then available on port 31000 on each host of the cluster, the result one is available on port 31001. + +Architecture +----- + +![Architecture diagram](architecture.png) + +* A Python webapp which lets you vote between two options +* A Redis queue which collects new votes +* A .NET worker which consumes votes and stores them in… +* A Postgres database backed by a Docker volume +* A Node.js webapp which shows the results of the voting in real time + + +Note +---- + +The voting application only accepts one vote per client. It does not register votes if a vote has already been submitted from a client. diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000000..bd456a0d77 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,28 @@ +# Docker +# Build a Docker image +# https://docs.microsoft.com/azure/devops/pipelines/languages/docker + +trigger: +- master + +resources: +- repo: self + +variables: + tag: '$(Build.BuildId)' + +stages: +- stage: Build + displayName: Build image + jobs: + - job: Build + displayName: Build + pool: + name: mainagent + steps: + - task: Docker@2 + inputs: + containerRegistry: 'ACR-new' + repository: 'acer-test' + command: 'buildAndPush' + Dockerfile: '**/Dockerfile' diff --git a/e2e.sh b/e2e.sh index 0862d42c36..666a2f0522 100755 --- a/e2e.sh +++ b/e2e.sh @@ -1,17 +1,17 @@ -#!/bin/bash - -cd e2e - -docker-compose down > /dev/null 2>&1 - -#sleep 10 - -docker-compose build -docker-compose up -d - -docker-compose ps - -docker-compose run --rm e2e - -docker-compose down - +#!/bin/bash + +cd e2e + +docker-compose down > /dev/null 2>&1 + +#sleep 10 + +docker-compose build +docker-compose up -d + +docker-compose ps + +docker-compose run --rm e2e + +docker-compose down + diff --git a/e2e/.env b/e2e/.env index ba78b2ea4c..d515ec853f 100644 --- a/e2e/.env +++ b/e2e/.env @@ -1,3 +1,3 @@ -VOTE_IMAGE=lfs261/vote:master -WORKER_IMAGE=lfs261/worker:master -RESULT_IMAGE=lfs261/result:master +VOTE_IMAGE=lfs261/vote:master +WORKER_IMAGE=lfs261/worker:master +RESULT_IMAGE=lfs261/result:master diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 1427ba2256..835fb14a12 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -1,60 +1,60 @@ -version: '2' - -services: - - e2e: - build: ./tests/ - depends_on: - - vote - - result - - worker - - db - - redis - networks: - - front-tier - - vote: - image: ${VOTE_IMAGE} - ports: ["80"] - depends_on: - - redis - - db - networks: - - front-tier - - back-tier - - result: - image: ${RESULT_IMAGE} - ports: ["4000"] - depends_on: - - redis - - db - networks: - - front-tier - - back-tier - - worker: - image: ${WORKER_IMAGE} - depends_on: - - redis - - db - networks: - - back-tier - - redis: - image: redis:alpine - ports: ["6379"] - networks: - - back-tier - - db: - image: postgres:9.4 - networks: - - back-tier - -volumes: - db-data: - -networks: - front-tier: - back-tier: +version: '2' + +services: + + e2e: + build: ./tests/ + depends_on: + - vote + - result + - worker + - db + - redis + networks: + - front-tier + + vote: + image: ${VOTE_IMAGE} + ports: ["80"] + depends_on: + - redis + - db + networks: + - front-tier + - back-tier + + result: + image: ${RESULT_IMAGE} + ports: ["4000"] + depends_on: + - redis + - db + networks: + - front-tier + - back-tier + + worker: + image: ${WORKER_IMAGE} + depends_on: + - redis + - db + networks: + - back-tier + + redis: + image: redis:alpine + ports: ["6379"] + networks: + - back-tier + + db: + image: postgres:9.4 + networks: + - back-tier + +volumes: + db-data: + +networks: + front-tier: + back-tier: diff --git a/e2e/tests/Dockerfile b/e2e/tests/Dockerfile index 2b0e82210b..ae344e082e 100644 --- a/e2e/tests/Dockerfile +++ b/e2e/tests/Dockerfile @@ -1,12 +1,12 @@ -FROM node:8.16.0-jessie-slim - -RUN apt-get update -qq && apt-get install -qy \ - ca-certificates \ - bzip2 \ - curl \ - libfontconfig \ - --no-install-recommends -RUN yarn global add phantomjs-prebuilt -ADD . /app -WORKDIR /app -CMD ./tests.sh +FROM node:8.16.0-jessie-slim + +RUN apt-get update -qq && apt-get install -qy \ + ca-certificates \ + bzip2 \ + curl \ + libfontconfig \ + --no-install-recommends +RUN yarn global add phantomjs-prebuilt +ADD . /app +WORKDIR /app +CMD ./tests.sh diff --git a/e2e/tests/render.js b/e2e/tests/render.js index 975137bf3b..27bc8a50d8 100644 --- a/e2e/tests/render.js +++ b/e2e/tests/render.js @@ -1,15 +1,15 @@ -var system = require('system'); -var page = require('webpage').create(); -var url = system.args[1]; - -page.onLoadFinished = function() { - setTimeout(function(){ - console.log(page.content); - phantom.exit(); - }, 1000); -}; - -page.open(url, function() { - page.evaluate(function() { - }); -}); +var system = require('system'); +var page = require('webpage').create(); +var url = system.args[1]; + +page.onLoadFinished = function() { + setTimeout(function(){ + console.log(page.content); + phantom.exit(); + }, 1000); +}; + +page.open(url, function() { + page.evaluate(function() { + }); +}); diff --git a/e2e/tests/tests.sh b/e2e/tests/tests.sh index e76354f5f8..f911f3a3a0 100755 --- a/e2e/tests/tests.sh +++ b/e2e/tests/tests.sh @@ -1,44 +1,44 @@ -#!/bin/bash - -current="" -next="" - -while ! timeout 1 bash -c "echo > /dev/tcp/vote/80"; do - sleep 1 -done - -# add initial vote -curl -sS -X POST --data "vote=a" http://vote > /dev/null - -current=`phantomjs render.js "http://result:4000/" | grep -i vote | cut -d ">" -f 4 | cut -d " " -f1` -next=`echo "$(($current + 1))"` - - echo -e "\n\n-----------------" - echo -e "Current Votes Count: $current" - echo -e "-----------------\n" - -echo -e " I: Submitting one more vote...\n" - -curl -sS -X POST --data "vote=b" http://vote > /dev/null -sleep 3 - -new=`phantomjs render.js "http://result:4000/" | grep -i vote | cut -d ">" -f 4 | cut -d " " -f1` - - - echo -e "\n\n-----------------" - echo -e "New Votes Count: $new" - echo -e "-----------------\n" - -echo -e "I: Checking if votes tally......\n" - -if [ "$next" -eq "$new" ]; then - echo -e "\\e[42m------------" - echo -e "\\e[92mTests passed" - echo -e "\\e[42m------------" - exit 0 -else - echo -e "\\e[41m------------" - echo -e "\\e[91mTests failed" - echo -e "\\e[41m------------" - exit 1 -fi +#!/bin/bash + +current="" +next="" + +while ! timeout 1 bash -c "echo > /dev/tcp/vote/80"; do + sleep 1 +done + +# add initial vote +curl -sS -X POST --data "vote=a" http://vote > /dev/null + +current=`phantomjs render.js "http://result:4000/" | grep -i vote | cut -d ">" -f 4 | cut -d " " -f1` +next=`echo "$(($current + 1))"` + + echo -e "\n\n-----------------" + echo -e "Current Votes Count: $current" + echo -e "-----------------\n" + +echo -e " I: Submitting one more vote...\n" + +curl -sS -X POST --data "vote=b" http://vote > /dev/null +sleep 3 + +new=`phantomjs render.js "http://result:4000/" | grep -i vote | cut -d ">" -f 4 | cut -d " " -f1` + + + echo -e "\n\n-----------------" + echo -e "New Votes Count: $new" + echo -e "-----------------\n" + +echo -e "I: Checking if votes tally......\n" + +if [ "$next" -eq "$new" ]; then + echo -e "\\e[42m------------" + echo -e "\\e[92mTests passed" + echo -e "\\e[42m------------" + exit 0 +else + echo -e "\\e[41m------------" + echo -e "\\e[91mTests failed" + echo -e "\\e[41m------------" + exit 1 +fi diff --git a/result/.vscode/launch.json b/result/.vscode/launch.json index c081024453..b450545d93 100644 --- a/result/.vscode/launch.json +++ b/result/.vscode/launch.json @@ -1,18 +1,18 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Attach", - "type": "node", - "request": "attach", - "port": 5858, - "address": "localhost", - "restart": true, - "sourceMaps": false, - "outDir": null, - "localRoot": "${workspaceRoot}", - "remoteRoot": "/app", - "timeout": 10000 - } - ] -} +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach", + "type": "node", + "request": "attach", + "port": 5858, + "address": "localhost", + "restart": true, + "sourceMaps": false, + "outDir": null, + "localRoot": "${workspaceRoot}", + "remoteRoot": "/app", + "timeout": 10000 + } + ] +} diff --git a/result/Dockerfile b/result/Dockerfile index 4f3713fc7a..5da9b12293 100644 --- a/result/Dockerfile +++ b/result/Dockerfile @@ -1,12 +1,12 @@ -FROM node:8.16.0-alpine - -WORKDIR /app - -COPY . . - -RUN npm install && \ - npm audit fix - -EXPOSE 4000 - -CMD npm start +FROM node:8.16.0-alpine + +WORKDIR /app + +COPY . . + +RUN npm install && \ + npm audit fix + +EXPOSE 4000 + +CMD npm start diff --git a/result/Dockerfile-scratch b/result/Dockerfile-scratch index d736db9db0..eb6a93b6d4 100644 --- a/result/Dockerfile-scratch +++ b/result/Dockerfile-scratch @@ -1,16 +1,16 @@ -FROM node:5.11.0-slim - -WORKDIR /app - -RUN npm install -g nodemon -ADD package.json /app/package.json -RUN npm config set registry http://registry.npmjs.org -RUN npm install && npm ls -RUN mv /app/node_modules /node_modules - -ADD . /app - -ENV PORT 80 -EXPOSE 80 - -CMD ["node", "server.js"] +FROM node:5.11.0-slim + +WORKDIR /app + +RUN npm install -g nodemon +ADD package.json /app/package.json +RUN npm config set registry http://registry.npmjs.org +RUN npm install && npm ls +RUN mv /app/node_modules /node_modules + +ADD . /app + +ENV PORT 80 +EXPOSE 80 + +CMD ["node", "server.js"] diff --git a/result/package.json b/result/package.json index f0ac5fb3a6..336eea4c2f 100644 --- a/result/package.json +++ b/result/package.json @@ -1,24 +1,24 @@ -{ - "name": "result", - "version": "1.1.0", - "description": "", - "main": "server.js", - "scripts": { - "test": "node_modules/.bin/mocha" - }, - "author": "", - "license": "MIT", - "dependencies": { - "body-parser": "^1.14.1", - "cookie-parser": "^1.4.0", - "express": "^4.13.3", - "method-override": "^2.3.5", - "async": "^1.5.0", - "pg": "^4.4.3", - "socket.io": "^1.3.7" - }, - "devDependencies": { - "chai": "^4.0.2", - "mocha": "^3.4.2" - } -} +{ + "name": "result", + "version": "1.1.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "node_modules/.bin/mocha" + }, + "author": "", + "license": "MIT", + "dependencies": { + "body-parser": "^1.14.1", + "cookie-parser": "^1.4.0", + "express": "^4.13.3", + "method-override": "^2.3.5", + "async": "^1.5.0", + "pg": "^4.4.3", + "socket.io": "^1.3.7" + }, + "devDependencies": { + "chai": "^4.0.2", + "mocha": "^3.4.2" + } +} diff --git a/result/server.js b/result/server.js index f3d7a0745c..28555ac50f 100644 --- a/result/server.js +++ b/result/server.js @@ -1,86 +1,86 @@ -var express = require('express'), - async = require('async'), - pg = require("pg"), - path = require("path"), - cookieParser = require('cookie-parser'), - bodyParser = require('body-parser'), - methodOverride = require('method-override'), - app = express(), - server = require('http').Server(app), - io = require('socket.io')(server); - -io.set('transports', ['polling']); - -var port = process.env.PORT || 4000; - -io.sockets.on('connection', function (socket) { - - socket.emit('message', { text : 'Welcome!' }); - - socket.on('subscribe', function (data) { - socket.join(data.channel); - }); -}); - -async.retry( - {times: 1000, interval: 1000}, - function(callback) { - pg.connect('postgres://postgres@db/postgres', function(err, client, done) { - if (err) { - console.error("Waiting for db"); - } - callback(err, client); - }); - }, - function(err, client) { - if (err) { - return console.error("Giving up"); - } - console.log("Connected to db"); - getVotes(client); - } -); - -function getVotes(client) { - client.query('SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote', [], function(err, result) { - if (err) { - console.error("Error performing query: " + err); - } else { - var votes = collectVotesFromResult(result); - io.sockets.emit("scores", JSON.stringify(votes)); - } - - setTimeout(function() {getVotes(client) }, 1000); - }); -} - -function collectVotesFromResult(result) { - var votes = {a: 0, b: 0}; - - result.rows.forEach(function (row) { - votes[row.vote] = parseInt(row.count); - }); - - return votes; -} - -app.use(cookieParser()); -app.use(bodyParser()); -app.use(methodOverride('X-HTTP-Method-Override')); -app.use(function(req, res, next) { - res.header("Access-Control-Allow-Origin", "*"); - res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS"); - next(); -}); - -app.use(express.static(__dirname + '/views')); - -app.get('/', function (req, res) { - res.sendFile(path.resolve(__dirname + '/views/index.html')); -}); - -server.listen(port, function () { - var port = server.address().port; - console.log('App running on port ' + port); -}); +var express = require('express'), + async = require('async'), + pg = require("pg"), + path = require("path"), + cookieParser = require('cookie-parser'), + bodyParser = require('body-parser'), + methodOverride = require('method-override'), + app = express(), + server = require('http').Server(app), + io = require('socket.io')(server); + +io.set('transports', ['polling']); + +var port = process.env.PORT || 4000; + +io.sockets.on('connection', function (socket) { + + socket.emit('message', { text : 'Welcome!' }); + + socket.on('subscribe', function (data) { + socket.join(data.channel); + }); +}); + +async.retry( + {times: 1000, interval: 1000}, + function(callback) { + pg.connect('postgres://postgres@db/postgres', function(err, client, done) { + if (err) { + console.error("Waiting for db"); + } + callback(err, client); + }); + }, + function(err, client) { + if (err) { + return console.error("Giving up"); + } + console.log("Connected to db"); + getVotes(client); + } +); + +function getVotes(client) { + client.query('SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote', [], function(err, result) { + if (err) { + console.error("Error performing query: " + err); + } else { + var votes = collectVotesFromResult(result); + io.sockets.emit("scores", JSON.stringify(votes)); + } + + setTimeout(function() {getVotes(client) }, 1000); + }); +} + +function collectVotesFromResult(result) { + var votes = {a: 0, b: 0}; + + result.rows.forEach(function (row) { + votes[row.vote] = parseInt(row.count); + }); + + return votes; +} + +app.use(cookieParser()); +app.use(bodyParser()); +app.use(methodOverride('X-HTTP-Method-Override')); +app.use(function(req, res, next) { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS"); + next(); +}); + +app.use(express.static(__dirname + '/views')); + +app.get('/', function (req, res) { + res.sendFile(path.resolve(__dirname + '/views/index.html')); +}); + +server.listen(port, function () { + var port = server.address().port; + console.log('App running on port ' + port); +}); diff --git a/result/sonar-project.properties b/result/sonar-project.properties index cd09fd2dd6..12b1c1b060 100644 --- a/result/sonar-project.properties +++ b/result/sonar-project.properties @@ -1,9 +1,9 @@ -sonar.projectKey=result -sonar.projectName=Instavote Results NodeJS App -sonar.projectVersion=1.0 -# Comma-separated paths to directories with sources (required) -sonar.sources=result -# Encoding of the source files -sonar.sourceEncoding=UTF-8 -sonar.java.binaries=. - +sonar.projectKey=result +sonar.projectName=Instavote Results NodeJS App +sonar.projectVersion=1.0 +# Comma-separated paths to directories with sources (required) +sonar.sources=result +# Encoding of the source files +sonar.sourceEncoding=UTF-8 +sonar.java.binaries=. + diff --git a/result/test/mock.test.js b/result/test/mock.test.js index f99dfdae97..675094da02 100644 --- a/result/test/mock.test.js +++ b/result/test/mock.test.js @@ -1,29 +1,29 @@ -const expect = require('chai').expect; - -describe('mock test 1', () => { - it('unit test 1', () => { - expect(true).to.be.true; - }); -}); - - -describe('mock test 2', () => { - it('unit test 2', () => { - expect(true).to.be.true; - }); -}); - -describe('mock test 3', () => { - it('unit test 3', () => { - expect(true).to.be.true; - }); -}); - - -describe('mock test 4', () => { - it('unit test 4', () => { - expect(true).to.be.true; - }); -}); - - +const expect = require('chai').expect; + +describe('mock test 1', () => { + it('unit test 1', () => { + expect(true).to.be.true; + }); +}); + + +describe('mock test 2', () => { + it('unit test 2', () => { + expect(true).to.be.true; + }); +}); + +describe('mock test 3', () => { + it('unit test 3', () => { + expect(true).to.be.true; + }); +}); + + +describe('mock test 4', () => { + it('unit test 4', () => { + expect(true).to.be.true; + }); +}); + + diff --git a/result/views/app.js b/result/views/app.js index 8b84cdda0e..280c7bf726 100644 --- a/result/views/app.js +++ b/result/views/app.js @@ -1,50 +1,50 @@ -var app = angular.module('catsvsdogs', []); -var socket = io.connect({transports:['polling']}); - -var bg1 = document.getElementById('background-stats-1'); -var bg2 = document.getElementById('background-stats-2'); - -app.controller('statsCtrl', function($scope){ - $scope.aPercent = 50; - $scope.bPercent = 50; - - var updateScores = function(){ - socket.on('scores', function (json) { - data = JSON.parse(json); - var a = parseInt(data.a || 0); - var b = parseInt(data.b || 0); - - var percentages = getPercentages(a, b); - - bg1.style.width = percentages.a + "%"; - bg2.style.width = percentages.b + "%"; - - $scope.$apply(function () { - $scope.aPercent = percentages.a; - $scope.bPercent = percentages.b; - $scope.total = a + b; - }); - }); - }; - - var init = function(){ - document.body.style.opacity=1; - updateScores(); - }; - socket.on('message',function(data){ - init(); - }); -}); - -function getPercentages(a, b) { - var result = {}; - - if (a + b > 0) { - result.a = Math.round(a / (a + b) * 100); - result.b = 100 - result.a; - } else { - result.a = result.b = 50; - } - - return result; +var app = angular.module('catsvsdogs', []); +var socket = io.connect({transports:['polling']}); + +var bg1 = document.getElementById('background-stats-1'); +var bg2 = document.getElementById('background-stats-2'); + +app.controller('statsCtrl', function($scope){ + $scope.aPercent = 50; + $scope.bPercent = 50; + + var updateScores = function(){ + socket.on('scores', function (json) { + data = JSON.parse(json); + var a = parseInt(data.a || 0); + var b = parseInt(data.b || 0); + + var percentages = getPercentages(a, b); + + bg1.style.width = percentages.a + "%"; + bg2.style.width = percentages.b + "%"; + + $scope.$apply(function () { + $scope.aPercent = percentages.a; + $scope.bPercent = percentages.b; + $scope.total = a + b; + }); + }); + }; + + var init = function(){ + document.body.style.opacity=1; + updateScores(); + }; + socket.on('message',function(data){ + init(); + }); +}); + +function getPercentages(a, b) { + var result = {}; + + if (a + b > 0) { + result.a = Math.round(a / (a + b) * 100); + result.b = 100 - result.a; + } else { + result.a = result.b = 50; + } + + return result; } \ No newline at end of file diff --git a/result/views/index.html b/result/views/index.html index c198a9085b..a1efbda005 100644 --- a/result/views/index.html +++ b/result/views/index.html @@ -1,43 +1,43 @@ - - - - - Cats vs Dogs -- Result - - - - - - - -
-
-
-
-
-
-
-
-
-
Emacs
-
{{aPercent | number:1}}%
-
-
-
-
Vi
-
{{bPercent | number:1}}%
-
-
-
-
-
- No votes yet - {{total}} vote - {{total}} votes -
- - - - - + + + + + Cats vs Dogs -- Result + + + + + + + +
+
+
+
+
+
+
+
+
+
Emacs
+
{{aPercent | number:1}}%
+
+
+
+
Vi
+
{{bPercent | number:1}}%
+
+
+
+
+
+ No votes yet + {{total}} vote + {{total}} votes +
+ + + + + diff --git a/result/views/socket.io.js b/result/views/socket.io.js index 67f5484fb1..2a9324644d 100644 --- a/result/views/socket.io.js +++ b/result/views/socket.io.js @@ -1,6988 +1,6988 @@ -!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 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":40,"object-component":41,"socket.io-parser":44}],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":36,"socket.io-parser":44,"to-array":48}],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":42}],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