Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db6a7e3e28 | |||
| d9aedfe7d3 | |||
| 805026360e | |||
| 3d37d21b5f | |||
| c33f0b52e7 | |||
| 1d795c0643 | |||
| c00c0eed1a | |||
| 47fb367486 | |||
| 5e16b38524 | |||
| c728f43c63 | |||
| d1458d0063 | |||
| 66d772a6bc | |||
| a670af376e | |||
| 16691395e0 | |||
| 3018cf8c1a | |||
| 6a126405bd | |||
| ee2f269c06 | |||
| 780cb8a9e6 | |||
| 388d9573fd | |||
| 578dba1987 | |||
| 124c9a7d88 | |||
| e53fa10397 | |||
| 1295668abb | |||
| 880257a431 | |||
| 8b3125bcf3 | |||
| 471b504799 | |||
| b713acbeff | |||
| ff736e9ad3 | |||
| 384c2772fb | |||
| e6d8d8f242 | |||
| ca8efbf657 | |||
| eb6d4e183b | |||
| 6d9deda9c5 | |||
| 96890fab97 | |||
| c0d23e27e4 | |||
| 2d2798954e | |||
| 44e5fa0b82 | |||
| 82cb31f3b6 | |||
| 9348dd5208 |
@@ -4,5 +4,8 @@
|
|||||||
other/.lein-deps-sum
|
other/.lein-deps-sum
|
||||||
other/classes
|
other/classes
|
||||||
other/lib
|
other/lib
|
||||||
|
other/node_modules
|
||||||
.project
|
.project
|
||||||
.pydevproject
|
.pydevproject
|
||||||
|
target.cfg
|
||||||
|
target.cfg.d
|
||||||
|
|||||||
+38
-2
@@ -1,8 +1,44 @@
|
|||||||
Changes
|
Changes
|
||||||
=======
|
=======
|
||||||
|
|
||||||
0.1.0
|
0.4.1 - Mar 12, 2013
|
||||||
-----------------
|
--------------------
|
||||||
|
|
||||||
|
* ***NOTE*** : 0.5.0 will drop Hixie protocol support
|
||||||
|
* add include/ directory and remove some dev files from source
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
0.4.0 - Mar 12, 2013
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* ***NOTE*** : 0.5.0 will drop Hixie protocol support
|
||||||
|
* use Buffer base64 support in Node.js implementation
|
||||||
|
|
||||||
|
0.3.0 - Jan 15, 2013
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* refactor into modules: websocket, websocketproxy
|
||||||
|
* switch to web-socket-js that uses IETF 6455
|
||||||
|
* change to MPL 2.0 license for include/*.js
|
||||||
|
* fix session recording
|
||||||
|
|
||||||
|
0.2.1 - Oct 15, 2012
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* re-released with updated version number
|
||||||
|
|
||||||
|
0.2.0 - Sep 17, 2012
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* Binary data support in websock.js
|
||||||
|
* Target config file/dir and multiple targets with token selector
|
||||||
|
* IPv6 fixes
|
||||||
|
* SSL target support
|
||||||
|
* Proxy to/from unix socket
|
||||||
|
|
||||||
|
|
||||||
|
0.1.0 - May 11, 2012
|
||||||
|
--------------------
|
||||||
|
|
||||||
* Initial versioned release.
|
* Initial versioned release.
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -1,11 +1,16 @@
|
|||||||
websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and
|
websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and
|
||||||
docs/LICENSE.LGPL-3) with the following exceptions:
|
docs/LICENSE.LGPL-3) with the following exceptions:
|
||||||
|
|
||||||
include/base64.js : Choice of MIT 1.1, GPL-2 or LGPL-2.1
|
include/websock.js : MPL 2.0
|
||||||
|
|
||||||
include/web-socket-js/ : New BSD license. Source code at
|
include/base64.js : MPL 2.0
|
||||||
|
|
||||||
|
include/des.js : Various BSD style licenses
|
||||||
|
|
||||||
|
include/web-socket-js/ : New BSD license (3-clause). Source code at
|
||||||
https://github.com/gimite/web-socket-js
|
https://github.com/gimite/web-socket-js
|
||||||
|
|
||||||
other/kumina.c : Simplified BSD license (2 clause).
|
other/kumina.c : Simplified BSD license (2 clause).
|
||||||
Original source at
|
Original source at
|
||||||
https://github.com/kumina/wsproxy
|
https://github.com/kumina/wsproxy
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
include CHANGES.txt *.py README.md LICENSE.txt
|
include CHANGES.txt websockify include README.md LICENSE.txt
|
||||||
|
|||||||
@@ -10,12 +10,26 @@ the target in both directions.
|
|||||||
|
|
||||||
### WebSockets binary data
|
### WebSockets binary data
|
||||||
|
|
||||||
Websockify supports all versions of the WebSockets protocol (Hixie and
|
Websockify 0.4.X supports all versions of the WebSockets protocol
|
||||||
HyBI). The older Hixie versions of the protocol only support UTF-8
|
(Hixie and HyBi). Starting with websockify 0.5.0, only the HyBi / IETF
|
||||||
text payloads. In order to transport binary data over UTF-8 an
|
6455 WebSocket protocol is supported.
|
||||||
encoding must used to encapsulate the data within UTF-8. Websockify
|
|
||||||
uses base64 to encode all traffic to and from the client. This does
|
The older Hixie versions of the protocol only support UTF-8 text
|
||||||
not affect the data between websockify and the server.
|
payloads. In order to transport binary data over UTF-8 an encoding
|
||||||
|
must used to encapsulate the data within UTF-8. With Hixie clients,
|
||||||
|
Websockify uses base64 to encode all traffic to and from the client.
|
||||||
|
This does not affect the data between websockify and the server.
|
||||||
|
|
||||||
|
With HyBi clients, websockify negotiates whether to base64 encode
|
||||||
|
traffic to and from the client via the subprotocol header
|
||||||
|
(Sec-WebSocket-Protocol). The valid subprotocol values are 'binary'
|
||||||
|
and 'base64' and if the client sends both then the server (the python
|
||||||
|
implementation) will prefer 'binary'. The 'binary' subprotocol
|
||||||
|
indicates that the data will be sent raw using binary WebSocket
|
||||||
|
frames. Some HyBi clients (such as the Flash fallback and older Chrome
|
||||||
|
and iOS versions) do not support binary data which is why the
|
||||||
|
negotiation is necessary.
|
||||||
|
|
||||||
|
|
||||||
### Encrypted WebSocket connections (wss://)
|
### Encrypted WebSocket connections (wss://)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,373 @@
|
|||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
- Go implementation
|
- Go implementation
|
||||||
|
- Rust implementation
|
||||||
- Support multiple targets that are selected by the path line. Some
|
- Add sub-protocol support to upstream einaros/ws module and use that
|
||||||
sort of wildcarding of ports too.
|
instead of the patched module.
|
||||||
|
|
||||||
- Support SSL targets too.
|
|
||||||
|
|
||||||
- wstelnet: support CSI L and CSI M
|
- wstelnet: support CSI L and CSI M
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -13,5 +13,12 @@ browser.
|
|||||||
|
|
||||||
Building web-socket-js emulator:
|
Building web-socket-js emulator:
|
||||||
|
|
||||||
cd include/web-socket-js/flash-src
|
cd include/web-socket-js/flash-src
|
||||||
mxmlc -static-link-runtime-shared-libraries WebSocketMain.as
|
mxmlc -static-link-runtime-shared-libraries WebSocketMain.as
|
||||||
|
|
||||||
|
Building release tarball:
|
||||||
|
- not really necessary since tagged revision can be downloaded
|
||||||
|
from github as tarballs
|
||||||
|
|
||||||
|
git archive --format=tar --prefix=websockify-${WVER}/ v${WVER} > websockify-${WVER}.tar
|
||||||
|
gzip websockify-${WVER}.tar
|
||||||
|
|||||||
+7
-4
@@ -1,7 +1,10 @@
|
|||||||
- Update setup.py and CHANGES.txt and commit
|
- Update setup.py and CHANGES.txt and commit
|
||||||
- Create version tag and tarball from tag
|
- Create version tag and tarball from tag
|
||||||
WVER=0.1.0
|
WVER=0.1.0
|
||||||
git tag ${WVER}
|
git tag v${WVER}
|
||||||
git archive --format=tar --prefix=websockify-${WVER}/ v${WVER} > websockify-${WVER}.tar
|
git push origin master
|
||||||
gzip websockify-${WVER}.tar
|
git push origin v${WVER}
|
||||||
- Upload tarball to repo
|
- Register with pypi.python.org (once):
|
||||||
|
python setup.py register
|
||||||
|
- Upload the source distribution to pypi
|
||||||
|
python setup.py sdist upload
|
||||||
|
|||||||
+32
-65
@@ -1,45 +1,8 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* Modified from:
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
*/
|
|
||||||
|
|
||||||
/* ***** BEGIN LICENSE BLOCK *****
|
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (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.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is Mozilla XML-RPC Client component.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is
|
|
||||||
* Digital Creations 2, Inc.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2000
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Martijn Pieters <mj@digicool.com> (original author)
|
|
||||||
* Samuel Sieb <samuel@sieb.net>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/*jslint white: false, bitwise: false, plusplus: false */
|
/*jslint white: false, bitwise: false, plusplus: false */
|
||||||
/*global console */
|
/*global console */
|
||||||
@@ -47,35 +10,37 @@
|
|||||||
var Base64 = {
|
var Base64 = {
|
||||||
|
|
||||||
/* Convert data (an array of integers) to a Base64 string. */
|
/* Convert data (an array of integers) to a Base64 string. */
|
||||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''),
|
||||||
base64Pad : '=',
|
base64Pad : '=',
|
||||||
|
|
||||||
encode: function (data) {
|
encode: function (data) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var result = '',
|
var result = '';
|
||||||
chrTable = Base64.toBase64Table.split(''),
|
var toBase64Table = Base64.toBase64Table;
|
||||||
pad = Base64.base64Pad,
|
var base64Pad = Base64.base64Pad;
|
||||||
length = data.length,
|
var length = data.length;
|
||||||
i;
|
var i;
|
||||||
// Convert every three bytes to 4 ascii characters.
|
// Convert every three bytes to 4 ascii characters.
|
||||||
|
/* BEGIN LOOP */
|
||||||
for (i = 0; i < (length - 2); i += 3) {
|
for (i = 0; i < (length - 2); i += 3) {
|
||||||
result += chrTable[data[i] >> 2];
|
result += toBase64Table[data[i] >> 2];
|
||||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||||
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
||||||
result += chrTable[data[i+2] & 0x3f];
|
result += toBase64Table[data[i+2] & 0x3f];
|
||||||
}
|
}
|
||||||
|
/* END LOOP */
|
||||||
|
|
||||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||||
if (length%3) {
|
if (length%3) {
|
||||||
i = length - (length%3);
|
i = length - (length%3);
|
||||||
result += chrTable[data[i] >> 2];
|
result += toBase64Table[data[i] >> 2];
|
||||||
if ((length%3) === 2) {
|
if ((length%3) === 2) {
|
||||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||||
result += chrTable[(data[i+1] & 0x0f) << 2];
|
result += toBase64Table[(data[i+1] & 0x0f) << 2];
|
||||||
result += pad;
|
result += base64Pad;
|
||||||
} else {
|
} else {
|
||||||
result += chrTable[(data[i] & 0x03) << 4];
|
result += toBase64Table[(data[i] & 0x03) << 4];
|
||||||
result += pad + pad;
|
result += base64Pad + base64Pad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,12 +62,12 @@ toBinaryTable : [
|
|||||||
decode: function (data, offset) {
|
decode: function (data, offset) {
|
||||||
"use strict";
|
"use strict";
|
||||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||||
var binTable = Base64.toBinaryTable,
|
var toBinaryTable = Base64.toBinaryTable;
|
||||||
pad = Base64.base64Pad,
|
var base64Pad = Base64.base64Pad;
|
||||||
result, result_length, idx, i, c, padding,
|
var result, result_length, idx, i, c, padding;
|
||||||
leftbits = 0, // number of bits decoded, but yet to be appended
|
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||||
leftdata = 0, // bits decoded, but yet to be appended
|
var leftdata = 0; // bits decoded, but yet to be appended
|
||||||
data_length = data.indexOf('=') - offset;
|
var data_length = data.indexOf('=') - offset;
|
||||||
|
|
||||||
if (data_length < 0) { data_length = data.length - offset; }
|
if (data_length < 0) { data_length = data.length - offset; }
|
||||||
|
|
||||||
@@ -111,9 +76,10 @@ decode: function (data, offset) {
|
|||||||
result = new Array(result_length);
|
result = new Array(result_length);
|
||||||
|
|
||||||
// Convert one by one.
|
// Convert one by one.
|
||||||
|
/* BEGIN LOOP */
|
||||||
for (idx = 0, i = offset; i < data.length; i++) {
|
for (idx = 0, i = offset; i < data.length; i++) {
|
||||||
c = binTable[data.charCodeAt(i) & 0x7f];
|
c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||||
padding = (data.charAt(i) === pad);
|
padding = (data.charAt(i) === base64Pad);
|
||||||
// Skip illegal characters and whitespace
|
// Skip illegal characters and whitespace
|
||||||
if (c === -1) {
|
if (c === -1) {
|
||||||
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||||
@@ -134,6 +100,7 @@ decode: function (data, offset) {
|
|||||||
leftdata &= (1 << leftbits) - 1;
|
leftdata &= (1 << leftbits) - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* END LOOP */
|
||||||
|
|
||||||
// If there are any bits left, the base64 string was corrupted
|
// If there are any bits left, the base64 string was corrupted
|
||||||
if (leftbits) {
|
if (leftbits) {
|
||||||
|
|||||||
+79
-3
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* from noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
@@ -57,6 +57,21 @@ if (!Array.prototype.map)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// requestAnimationFrame shim with setTimeout fallback
|
||||||
|
//
|
||||||
|
|
||||||
|
window.requestAnimFrame = (function(){
|
||||||
|
return window.requestAnimationFrame ||
|
||||||
|
window.webkitRequestAnimationFrame ||
|
||||||
|
window.mozRequestAnimationFrame ||
|
||||||
|
window.oRequestAnimationFrame ||
|
||||||
|
window.msRequestAnimationFrame ||
|
||||||
|
function(callback){
|
||||||
|
window.setTimeout(callback, 1000 / 60);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ------------------------------------------------------
|
* ------------------------------------------------------
|
||||||
* Namespaced in Util
|
* Namespaced in Util
|
||||||
@@ -131,6 +146,8 @@ Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
|
|||||||
}
|
}
|
||||||
} else if (type in {'integer':1, 'int':1}) {
|
} else if (type in {'integer':1, 'int':1}) {
|
||||||
val = parseInt(val, 10);
|
val = parseInt(val, 10);
|
||||||
|
} else if (type === 'str') {
|
||||||
|
val = String(val);
|
||||||
} else if (type === 'func') {
|
} else if (type === 'func') {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
val = function () {};
|
val = function () {};
|
||||||
@@ -190,6 +207,65 @@ Util.conf_defaults = function(cfg, api, defaults, arr) {
|
|||||||
* Cross-browser routines
|
* Cross-browser routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Dynamically load scripts without using document.write()
|
||||||
|
// Reference: http://unixpapa.com/js/dyna.html
|
||||||
|
//
|
||||||
|
// Handles the case where load_scripts is invoked from a script that
|
||||||
|
// itself is loaded via load_scripts. Once all scripts are loaded the
|
||||||
|
// window.onscriptsloaded handler is called (if set).
|
||||||
|
Util.get_include_uri = function() {
|
||||||
|
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
|
||||||
|
}
|
||||||
|
Util._loading_scripts = [];
|
||||||
|
Util._pending_scripts = [];
|
||||||
|
Util.load_scripts = function(files) {
|
||||||
|
var head = document.getElementsByTagName('head')[0], script,
|
||||||
|
ls = Util._loading_scripts, ps = Util._pending_scripts;
|
||||||
|
for (var f=0; f<files.length; f++) {
|
||||||
|
script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src = Util.get_include_uri() + files[f];
|
||||||
|
//console.log("loading script: " + script.src);
|
||||||
|
script.onload = script.onreadystatechange = function (e) {
|
||||||
|
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
|
||||||
|
ls[0].readyState === 'complete')) {
|
||||||
|
// For IE, append the script to trigger execution
|
||||||
|
var s = ls.shift();
|
||||||
|
//console.log("loaded script: " + s.src);
|
||||||
|
head.appendChild(s);
|
||||||
|
}
|
||||||
|
if (!this.readyState ||
|
||||||
|
(Util.Engine.presto && this.readyState === 'loaded') ||
|
||||||
|
this.readyState === 'complete') {
|
||||||
|
if (ps.indexOf(this) >= 0) {
|
||||||
|
this.onload = this.onreadystatechange = null;
|
||||||
|
//console.log("completed script: " + this.src);
|
||||||
|
ps.splice(ps.indexOf(this), 1);
|
||||||
|
|
||||||
|
// Call window.onscriptsload after last script loads
|
||||||
|
if (ps.length === 0 && window.onscriptsload) {
|
||||||
|
window.onscriptsload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// In-order script execution tricks
|
||||||
|
if (Util.Engine.trident) {
|
||||||
|
// For IE wait until readyState is 'loaded' before
|
||||||
|
// appending it which will trigger execution
|
||||||
|
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
|
||||||
|
ls.push(script);
|
||||||
|
} else {
|
||||||
|
// For webkit and firefox set async=false and append now
|
||||||
|
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
|
||||||
|
script.async = false;
|
||||||
|
head.appendChild(script);
|
||||||
|
}
|
||||||
|
ps.push(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get DOM element position on page
|
// Get DOM element position on page
|
||||||
Util.getPosition = function (obj) {
|
Util.getPosition = function (obj) {
|
||||||
var x = 0, y = 0;
|
var x = 0, y = 0;
|
||||||
|
|||||||
Binary file not shown.
@@ -1,49 +1,69 @@
|
|||||||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||||
// License: New BSD License
|
// License: New BSD License
|
||||||
// Reference: http://dev.w3.org/html5/websockets/
|
// Reference: http://dev.w3.org/html5/websockets/
|
||||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
|
// Reference: http://tools.ietf.org/html/rfc6455
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
if (window.WebSocket) return;
|
if (window.WEB_SOCKET_FORCE_FLASH) {
|
||||||
|
// Keeps going.
|
||||||
var console = window.console;
|
} else if (window.WebSocket) {
|
||||||
if (!console || !console.log || !console.error) {
|
return;
|
||||||
console = {log: function(){ }, error: function(){ }};
|
} else if (window.MozWebSocket) {
|
||||||
|
// Firefox.
|
||||||
|
window.WebSocket = MozWebSocket;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
|
var logger;
|
||||||
console.error("Flash Player >= 10.0.0 is required.");
|
if (window.WEB_SOCKET_LOGGER) {
|
||||||
|
logger = WEB_SOCKET_LOGGER;
|
||||||
|
} else if (window.console && window.console.log && window.console.error) {
|
||||||
|
// In some environment, console is defined but console.log or console.error is missing.
|
||||||
|
logger = window.console;
|
||||||
|
} else {
|
||||||
|
logger = {log: function(){ }, error: function(){ }};
|
||||||
|
}
|
||||||
|
|
||||||
|
// swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
|
||||||
|
if (swfobject.getFlashPlayerVersion().major < 10) {
|
||||||
|
logger.error("Flash Player >= 10.0.0 is required.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (location.protocol == "file:") {
|
if (location.protocol == "file:") {
|
||||||
console.error(
|
logger.error(
|
||||||
"WARNING: web-socket-js doesn't work in file:///... URL " +
|
"WARNING: web-socket-js doesn't work in file:///... URL " +
|
||||||
"unless you set Flash Security Settings properly. " +
|
"unless you set Flash Security Settings properly. " +
|
||||||
"Open the page via Web server i.e. http://...");
|
"Open the page via Web server i.e. http://...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a faux web socket.
|
* Our own implementation of WebSocket class using Flash.
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {string} protocol
|
* @param {array or string} protocols
|
||||||
* @param {string} proxyHost
|
* @param {string} proxyHost
|
||||||
* @param {int} proxyPort
|
* @param {int} proxyPort
|
||||||
* @param {string} headers
|
* @param {string} headers
|
||||||
*/
|
*/
|
||||||
WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
|
window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.__id = WebSocket.__nextId++;
|
self.__id = WebSocket.__nextId++;
|
||||||
WebSocket.__instances[self.__id] = self;
|
WebSocket.__instances[self.__id] = self;
|
||||||
self.readyState = WebSocket.CONNECTING;
|
self.readyState = WebSocket.CONNECTING;
|
||||||
self.bufferedAmount = 0;
|
self.bufferedAmount = 0;
|
||||||
self.__events = {};
|
self.__events = {};
|
||||||
|
if (!protocols) {
|
||||||
|
protocols = [];
|
||||||
|
} else if (typeof protocols == "string") {
|
||||||
|
protocols = [protocols];
|
||||||
|
}
|
||||||
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
|
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
|
||||||
// Otherwise, when onopen fires immediately, onopen is called before it is set.
|
// Otherwise, when onopen fires immediately, onopen is called before it is set.
|
||||||
setTimeout(function() {
|
self.__createTask = setTimeout(function() {
|
||||||
WebSocket.__addTask(function() {
|
WebSocket.__addTask(function() {
|
||||||
|
self.__createTask = null;
|
||||||
WebSocket.__flash.create(
|
WebSocket.__flash.create(
|
||||||
self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null);
|
self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
@@ -78,6 +98,12 @@
|
|||||||
* Close this web socket gracefully.
|
* Close this web socket gracefully.
|
||||||
*/
|
*/
|
||||||
WebSocket.prototype.close = function() {
|
WebSocket.prototype.close = function() {
|
||||||
|
if (this.__createTask) {
|
||||||
|
clearTimeout(this.__createTask);
|
||||||
|
this.__createTask = null;
|
||||||
|
this.readyState = WebSocket.CLOSED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
|
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -131,7 +157,7 @@
|
|||||||
events[i](event);
|
events[i](event);
|
||||||
}
|
}
|
||||||
var handler = this["on" + event.type];
|
var handler = this["on" + event.type];
|
||||||
if (handler) handler(event);
|
if (handler) handler.apply(this, [event]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,16 +165,22 @@
|
|||||||
* @param {Object} flashEvent
|
* @param {Object} flashEvent
|
||||||
*/
|
*/
|
||||||
WebSocket.prototype.__handleEvent = function(flashEvent) {
|
WebSocket.prototype.__handleEvent = function(flashEvent) {
|
||||||
|
|
||||||
if ("readyState" in flashEvent) {
|
if ("readyState" in flashEvent) {
|
||||||
this.readyState = flashEvent.readyState;
|
this.readyState = flashEvent.readyState;
|
||||||
}
|
}
|
||||||
|
if ("protocol" in flashEvent) {
|
||||||
|
this.protocol = flashEvent.protocol;
|
||||||
|
}
|
||||||
|
|
||||||
var jsEvent;
|
var jsEvent;
|
||||||
if (flashEvent.type == "open" || flashEvent.type == "error") {
|
if (flashEvent.type == "open" || flashEvent.type == "error") {
|
||||||
jsEvent = this.__createSimpleEvent(flashEvent.type);
|
jsEvent = this.__createSimpleEvent(flashEvent.type);
|
||||||
} else if (flashEvent.type == "close") {
|
} else if (flashEvent.type == "close") {
|
||||||
// TODO implement jsEvent.wasClean
|
|
||||||
jsEvent = this.__createSimpleEvent("close");
|
jsEvent = this.__createSimpleEvent("close");
|
||||||
|
jsEvent.wasClean = flashEvent.wasClean ? true : false;
|
||||||
|
jsEvent.code = flashEvent.code;
|
||||||
|
jsEvent.reason = flashEvent.reason;
|
||||||
} else if (flashEvent.type == "message") {
|
} else if (flashEvent.type == "message") {
|
||||||
var data = decodeURIComponent(flashEvent.message);
|
var data = decodeURIComponent(flashEvent.message);
|
||||||
jsEvent = this.__createMessageEvent("message", data);
|
jsEvent = this.__createMessageEvent("message", data);
|
||||||
@@ -157,6 +189,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchEvent(jsEvent);
|
this.dispatchEvent(jsEvent);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocket.prototype.__createSimpleEvent = function(type) {
|
WebSocket.prototype.__createSimpleEvent = function(type) {
|
||||||
@@ -188,6 +221,9 @@
|
|||||||
WebSocket.CLOSING = 2;
|
WebSocket.CLOSING = 2;
|
||||||
WebSocket.CLOSED = 3;
|
WebSocket.CLOSED = 3;
|
||||||
|
|
||||||
|
// Field to check implementation of WebSocket.
|
||||||
|
WebSocket.__isFlashImplementation = true;
|
||||||
|
WebSocket.__initialized = false;
|
||||||
WebSocket.__flash = null;
|
WebSocket.__flash = null;
|
||||||
WebSocket.__instances = {};
|
WebSocket.__instances = {};
|
||||||
WebSocket.__tasks = [];
|
WebSocket.__tasks = [];
|
||||||
@@ -207,16 +243,31 @@
|
|||||||
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
|
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
|
||||||
*/
|
*/
|
||||||
WebSocket.__initialize = function() {
|
WebSocket.__initialize = function() {
|
||||||
if (WebSocket.__flash) return;
|
|
||||||
|
if (WebSocket.__initialized) return;
|
||||||
|
WebSocket.__initialized = true;
|
||||||
|
|
||||||
if (WebSocket.__swfLocation) {
|
if (WebSocket.__swfLocation) {
|
||||||
// For backword compatibility.
|
// For backword compatibility.
|
||||||
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
|
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
|
||||||
}
|
}
|
||||||
if (!window.WEB_SOCKET_SWF_LOCATION) {
|
if (!window.WEB_SOCKET_SWF_LOCATION) {
|
||||||
console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
|
logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
|
||||||
|
!WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
|
||||||
|
WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
|
||||||
|
var swfHost = RegExp.$1;
|
||||||
|
if (location.host != swfHost) {
|
||||||
|
logger.error(
|
||||||
|
"[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
|
||||||
|
"('" + location.host + "' != '" + swfHost + "'). " +
|
||||||
|
"See also 'How to host HTML file and SWF file in different domains' section " +
|
||||||
|
"in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
|
||||||
|
"by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
|
||||||
|
}
|
||||||
|
}
|
||||||
var container = document.createElement("div");
|
var container = document.createElement("div");
|
||||||
container.id = "webSocketContainer";
|
container.id = "webSocketContainer";
|
||||||
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
|
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
|
||||||
@@ -250,9 +301,11 @@
|
|||||||
null,
|
null,
|
||||||
function(e) {
|
function(e) {
|
||||||
if (!e.success) {
|
if (!e.success) {
|
||||||
console.error("[WebSocket] swfobject.embedSWF failed");
|
logger.error("[WebSocket] swfobject.embedSWF failed");
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,7 +340,7 @@
|
|||||||
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
|
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
return true;
|
return true;
|
||||||
@@ -295,12 +348,12 @@
|
|||||||
|
|
||||||
// Called by Flash.
|
// Called by Flash.
|
||||||
WebSocket.__log = function(message) {
|
WebSocket.__log = function(message) {
|
||||||
console.log(decodeURIComponent(message));
|
logger.log(decodeURIComponent(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Called by Flash.
|
// Called by Flash.
|
||||||
WebSocket.__error = function(message) {
|
WebSocket.__error = function(message) {
|
||||||
console.error(decodeURIComponent(message));
|
logger.error(decodeURIComponent(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocket.__addTask = function(task) {
|
WebSocket.__addTask = function(task) {
|
||||||
@@ -327,15 +380,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
|
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
|
||||||
if (window.addEventListener) {
|
// NOTE:
|
||||||
window.addEventListener("load", function(){
|
// This fires immediately if web_socket.js is dynamically loaded after
|
||||||
WebSocket.__initialize();
|
// the document is loaded.
|
||||||
}, false);
|
swfobject.addDomLoadEvent(function() {
|
||||||
} else {
|
WebSocket.__initialize();
|
||||||
window.attachEvent("onload", function(){
|
});
|
||||||
WebSocket.__initialize();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
+25
-21
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Websock: high-performance binary WebSockets
|
* Websock: high-performance binary WebSockets
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* Websock is similar to the standard WebSocket object but Websock
|
* Websock is similar to the standard WebSocket object but Websock
|
||||||
* enables communication with raw TCP sockets (i.e. the binary stream)
|
* enables communication with raw TCP sockets (i.e. the binary stream)
|
||||||
@@ -35,23 +35,14 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
|||||||
|
|
||||||
Websock_native = false;
|
Websock_native = false;
|
||||||
(function () {
|
(function () {
|
||||||
function get_INCLUDE_URI() {
|
window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
|
||||||
return (typeof INCLUDE_URI !== "undefined") ?
|
|
||||||
INCLUDE_URI : "include/";
|
|
||||||
}
|
|
||||||
|
|
||||||
var start = "<script src='" + get_INCLUDE_URI(),
|
|
||||||
end = "'><\/script>", extra = "";
|
|
||||||
|
|
||||||
window.WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
|
|
||||||
"web-socket-js/WebSocketMain.swf";
|
"web-socket-js/WebSocketMain.swf";
|
||||||
if (Util.Engine.trident) {
|
if (Util.Engine.trident) {
|
||||||
Util.Debug("Forcing uncached load of WebSocketMain.swf");
|
Util.Debug("Forcing uncached load of WebSocketMain.swf");
|
||||||
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
|
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
|
||||||
}
|
}
|
||||||
extra += start + "web-socket-js/swfobject.js" + end;
|
Util.load_scripts(["web-socket-js/swfobject.js",
|
||||||
extra += start + "web-socket-js/web_socket.js" + end;
|
"web-socket-js/web_socket.js"]);
|
||||||
document.write(extra);
|
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +52,7 @@ function Websock() {
|
|||||||
|
|
||||||
var api = {}, // Public API
|
var api = {}, // Public API
|
||||||
websocket = null, // WebSocket object
|
websocket = null, // WebSocket object
|
||||||
mode = 'base64',
|
mode = 'base64', // Current WebSocket mode: 'binary', 'base64'
|
||||||
rQ = [], // Receive queue
|
rQ = [], // Receive queue
|
||||||
rQi = 0, // Receive queue index
|
rQi = 0, // Receive queue index
|
||||||
rQmax = 10000, // Max receive queue size before compacting
|
rQmax = 10000, // Max receive queue size before compacting
|
||||||
@@ -168,10 +159,11 @@ function rQwait(msg, num, goback) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
function encode_message() {
|
function encode_message() {
|
||||||
/* base64 encode */
|
|
||||||
if (mode === 'binary') {
|
if (mode === 'binary') {
|
||||||
|
// Put in a binary arraybuffer
|
||||||
return (new Uint8Array(sQ)).buffer;
|
return (new Uint8Array(sQ)).buffer;
|
||||||
} else {
|
} else {
|
||||||
|
// base64 encode
|
||||||
return Base64.encode(sQ);
|
return Base64.encode(sQ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +172,10 @@ function decode_message(data) {
|
|||||||
//Util.Debug(">> decode_message: " + data);
|
//Util.Debug(">> decode_message: " + data);
|
||||||
if (mode === 'binary') {
|
if (mode === 'binary') {
|
||||||
// push arraybuffer values onto the end
|
// push arraybuffer values onto the end
|
||||||
rQ.push.apply(rQ, (new Uint8Array(data)));
|
var u8 = new Uint8Array(data);
|
||||||
|
for (var i = 0; i < u8.length; i++) {
|
||||||
|
rQ.push(u8[i]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// base64 decode and concat to the end
|
// base64 decode and concat to the end
|
||||||
rQ = rQ.concat(Base64.decode(data, 0));
|
rQ = rQ.concat(Base64.decode(data, 0));
|
||||||
@@ -310,11 +305,19 @@ function init(protocols) {
|
|||||||
throw("WebSocket binary sub-protocol requested but not supported");
|
throw("WebSocket binary sub-protocol requested but not supported");
|
||||||
}
|
}
|
||||||
if (typeof(protocols) === "object") {
|
if (typeof(protocols) === "object") {
|
||||||
|
var new_protocols = [];
|
||||||
for (var i = 0; i < protocols.length; i++) {
|
for (var i = 0; i < protocols.length; i++) {
|
||||||
if (protocols[i] === 'binary') {
|
if (protocols[i] === 'binary') {
|
||||||
throw("WebSocket binary sub-protocol requested but not supported");
|
Util.Error("Skipping unsupported WebSocket binary sub-protocol");
|
||||||
|
} else {
|
||||||
|
new_protocols.push(protocols[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (new_protocols.length > 0) {
|
||||||
|
protocols = new_protocols;
|
||||||
|
} else {
|
||||||
|
throw("Only WebSocket binary sub-protocol was requested and not supported.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,6 +331,9 @@ function open(uri, protocols) {
|
|||||||
websocket = {};
|
websocket = {};
|
||||||
} else {
|
} else {
|
||||||
websocket = new WebSocket(uri, protocols);
|
websocket = new WebSocket(uri, protocols);
|
||||||
|
if (protocols.indexOf('binary') >= 0) {
|
||||||
|
websocket.binaryType = 'arraybuffer';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
websocket.onmessage = recv_message;
|
websocket.onmessage = recv_message;
|
||||||
@@ -340,9 +346,6 @@ function open(uri, protocols) {
|
|||||||
mode = 'base64';
|
mode = 'base64';
|
||||||
Util.Error("Server select no sub-protocol!: " + websocket.protocol);
|
Util.Error("Server select no sub-protocol!: " + websocket.protocol);
|
||||||
}
|
}
|
||||||
if (mode === 'binary') {
|
|
||||||
websocket.binaryType = 'arraybuffer';
|
|
||||||
}
|
|
||||||
eventHandlers.open();
|
eventHandlers.open();
|
||||||
Util.Debug("<< WebSock.onopen");
|
Util.Debug("<< WebSock.onopen");
|
||||||
};
|
};
|
||||||
@@ -371,8 +374,9 @@ function close() {
|
|||||||
|
|
||||||
// Override internal functions for testing
|
// Override internal functions for testing
|
||||||
// Takes a send function, returns reference to recv function
|
// Takes a send function, returns reference to recv function
|
||||||
function testMode(override_send) {
|
function testMode(override_send, data_mode) {
|
||||||
test_mode = true;
|
test_mode = true;
|
||||||
|
mode = data_mode;
|
||||||
api.send = override_send;
|
api.send = override_send;
|
||||||
api.close = function () {};
|
api.close = function () {};
|
||||||
return recv_message;
|
return recv_message;
|
||||||
|
|||||||
+78
-10
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* from noVNC: HTML5 VNC client
|
* from noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2010 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
@@ -37,14 +37,16 @@ if (!window.$D) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// init log level reading the logging HTTP param
|
// init log level reading the logging HTTP param
|
||||||
WebUtil.init_logging = function() {
|
WebUtil.init_logging = function(level) {
|
||||||
Util._log_level = (document.location.href.match(
|
if (typeof level !== "undefined") {
|
||||||
/logging=([A-Za-z0-9\._\-]*)/) ||
|
Util._log_level = level;
|
||||||
['', Util._log_level])[1];
|
} else {
|
||||||
|
Util._log_level = (document.location.href.match(
|
||||||
|
/logging=([A-Za-z0-9\._\-]*)/) ||
|
||||||
|
['', Util._log_level])[1];
|
||||||
|
}
|
||||||
Util.init_logging();
|
Util.init_logging();
|
||||||
};
|
};
|
||||||
WebUtil.init_logging();
|
|
||||||
|
|
||||||
|
|
||||||
WebUtil.dirObj = function (obj, depth, parent) {
|
WebUtil.dirObj = function (obj, depth, parent) {
|
||||||
@@ -75,9 +77,14 @@ WebUtil.dirObj = function (obj, depth, parent) {
|
|||||||
|
|
||||||
// Read a query string variable
|
// Read a query string variable
|
||||||
WebUtil.getQueryVar = function(name, defVal) {
|
WebUtil.getQueryVar = function(name, defVal) {
|
||||||
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
|
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'),
|
||||||
|
match = document.location.href.match(re);
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
return (document.location.href.match(re) || ['',defVal])[1];
|
if (match) {
|
||||||
|
return decodeURIComponent(match[1]);
|
||||||
|
} else {
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -113,6 +120,67 @@ WebUtil.eraseCookie = function(name) {
|
|||||||
WebUtil.createCookie(name,"",-1);
|
WebUtil.createCookie(name,"",-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting handling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
WebUtil.initSettings = function(callback) {
|
||||||
|
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
window.chrome.storage.sync.get(function (cfg) {
|
||||||
|
WebUtil.settings = cfg;
|
||||||
|
console.log(WebUtil.settings);
|
||||||
|
if (callback) {
|
||||||
|
callback.apply(this, callbackArgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No-op
|
||||||
|
if (callback) {
|
||||||
|
callback.apply(this, callbackArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// No days means only for this browser session
|
||||||
|
WebUtil.writeSetting = function(name, value) {
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
//console.log("writeSetting:", name, value);
|
||||||
|
if (WebUtil.settings[name] !== value) {
|
||||||
|
WebUtil.settings[name] = value;
|
||||||
|
window.chrome.storage.sync.set(WebUtil.settings);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(name, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.readSetting = function(name, defaultValue) {
|
||||||
|
var value;
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
value = WebUtil.settings[name];
|
||||||
|
} else {
|
||||||
|
value = localStorage.getItem(name);
|
||||||
|
}
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
if (value === null && typeof defaultValue !== undefined) {
|
||||||
|
return defaultValue;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.eraseSetting = function(name) {
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
window.chrome.storage.sync.remove(name);
|
||||||
|
delete WebUtil.settings[name];
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alternate stylesheet selection
|
* Alternate stylesheet selection
|
||||||
*/
|
*/
|
||||||
|
|||||||
Regular → Executable
+79
-24
@@ -1,53 +1,83 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
// A WebSocket to TCP socket proxy
|
// A WebSocket to TCP socket proxy
|
||||||
// Copyright 2012 Joel Martin
|
// Copyright 2012 Joel Martin
|
||||||
// Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
// Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
||||||
|
|
||||||
// Known to work with node 0.6
|
// Known to work with node 0.8.9
|
||||||
// Requires node modules: ws, base64, optimist and policyfile
|
// Requires node modules: ws, optimist and policyfile
|
||||||
// npm install ws base64 optimist policyfile
|
// npm install ws optimist policyfile
|
||||||
|
//
|
||||||
|
// NOTE:
|
||||||
|
// This version requires a patched version of einaros/ws that supports
|
||||||
|
// subprotocol negotiation. You can use the patched version like this:
|
||||||
|
//
|
||||||
|
// cd websockify/other
|
||||||
|
// git clone https://github.com/kanaka/ws
|
||||||
|
// npm link ./ws
|
||||||
|
|
||||||
|
|
||||||
var argv = require('optimist').argv,
|
var argv = require('optimist').argv,
|
||||||
net = require('net'),
|
net = require('net'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
|
https = require('https'),
|
||||||
url = require('url'),
|
url = require('url'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
policyfile = require('policyfile'),
|
policyfile = require('policyfile'),
|
||||||
|
|
||||||
base64 = require('base64/build/Release/base64'),
|
|
||||||
Buffer = require('buffer').Buffer,
|
Buffer = require('buffer').Buffer,
|
||||||
WebSocketServer = require('ws').Server,
|
WebSocketServer = require('ws').Server,
|
||||||
|
|
||||||
httpServer, wsServer,
|
webServer, wsServer,
|
||||||
source_host, source_port, target_host, target_port,
|
source_host, source_port, target_host, target_port,
|
||||||
web_path = null;
|
web_path = null;
|
||||||
|
|
||||||
|
|
||||||
// Handle new WebSocket client
|
// Handle new WebSocket client
|
||||||
new_client = function(client) {
|
new_client = function(client) {
|
||||||
console.log('WebSocket client connected');
|
var clientAddr = client._socket.remoteAddress, log;
|
||||||
//console.log('protocol: ' + client.protocol);
|
log = function (msg) {
|
||||||
|
console.log(' ' + clientAddr + ': '+ msg);
|
||||||
|
};
|
||||||
|
log('WebSocket connection');
|
||||||
|
log('Version ' + client.protocolVersion + ', subprotocol: ' + client.protocol);
|
||||||
|
|
||||||
var target = net.createConnection(target_port,target_host);
|
var target = net.createConnection(target_port,target_host, function() {
|
||||||
target.on('begin', function() {
|
log('connected to target');
|
||||||
console.log('connected to target');
|
|
||||||
});
|
});
|
||||||
target.on('data', function(data) {
|
target.on('data', function(data) {
|
||||||
client.send(base64.encode(new Buffer(data)));
|
//log("sending message: " + data);
|
||||||
|
try {
|
||||||
|
if (client.protocol === 'base64') {
|
||||||
|
client.send(new Buffer(data).toString('base64'));
|
||||||
|
} else {
|
||||||
|
client.send(data,{binary: true});
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
log("Client closed, cleaning up target");
|
||||||
|
target.end();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
target.on('end', function() {
|
target.on('end', function() {
|
||||||
console.log('target disconnected');
|
log('target disconnected');
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on('message', function(msg) {
|
client.on('message', function(msg) {
|
||||||
//console.log('got some message');
|
//log('got message: ' + msg);
|
||||||
target.write(base64.decode(msg),'binary');
|
if (client.protocol === 'base64') {
|
||||||
|
target.write(new Buffer(msg, 'base64'));
|
||||||
|
} else {
|
||||||
|
target.write(msg,'binary');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
client.on('close', function(code, reason) {
|
client.on('close', function(code, reason) {
|
||||||
console.log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
|
log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
|
||||||
|
target.end();
|
||||||
});
|
});
|
||||||
client.on('error', function(a) {
|
client.on('error', function(a) {
|
||||||
console.log('WebSocket client error: ' + a);
|
log('WebSocket client error: ' + a);
|
||||||
|
target.end();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,7 +103,7 @@ http_request = function (request, response) {
|
|||||||
var uri = url.parse(request.url).pathname
|
var uri = url.parse(request.url).pathname
|
||||||
, filename = path.join(argv.web, uri);
|
, filename = path.join(argv.web, uri);
|
||||||
|
|
||||||
path.exists(filename, function(exists) {
|
fs.exists(filename, function(exists) {
|
||||||
if(!exists) {
|
if(!exists) {
|
||||||
return http_error(response, 404, "404 Not Found");
|
return http_error(response, 404, "404 Not Found");
|
||||||
}
|
}
|
||||||
@@ -94,10 +124,25 @@ http_request = function (request, response) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Select 'binary' or 'base64' subprotocol, preferring 'binary'
|
||||||
|
selectProtocol = function(protocols, callback) {
|
||||||
|
var plist = protocols ? protocols.split(',') : "";
|
||||||
|
var plist = protocols.split(',');
|
||||||
|
if (plist.indexOf('binary') >= 0) {
|
||||||
|
callback(true, 'binary');
|
||||||
|
} else if (plist.indexOf('base64') >= 0) {
|
||||||
|
callback(true, 'base64');
|
||||||
|
} else {
|
||||||
|
console.log("Client must support 'binary' or 'base64' protocol");
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse source and target arguments into parts
|
// parse source and target arguments into parts
|
||||||
source_arg = argv._[0].toString();
|
|
||||||
target_arg = argv._[1].toString();
|
|
||||||
try {
|
try {
|
||||||
|
source_arg = argv._[0].toString();
|
||||||
|
target_arg = argv._[1].toString();
|
||||||
|
|
||||||
var idx;
|
var idx;
|
||||||
idx = source_arg.indexOf(":");
|
idx = source_arg.indexOf(":");
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
@@ -119,7 +164,7 @@ try {
|
|||||||
throw("illegal port");
|
throw("illegal port");
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("wsproxy.py [source_addr:]source_port target_addr:target_port");
|
console.error("websockify.js [--web web_dir] [--cert cert.pem [--key key.pem]] [source_addr:]source_port target_addr:target_port");
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +175,21 @@ if (argv.web) {
|
|||||||
console.log(" - Web server active. Serving: " + argv.web);
|
console.log(" - Web server active. Serving: " + argv.web);
|
||||||
}
|
}
|
||||||
|
|
||||||
httpServer = http.createServer(http_request);
|
if (argv.cert) {
|
||||||
httpServer.listen(source_port, function() {
|
argv.key = argv.key || argv.cert;
|
||||||
wsServer = new WebSocketServer({server: httpServer});
|
var cert = fs.readFileSync(argv.cert),
|
||||||
|
key = fs.readFileSync(argv.key);
|
||||||
|
console.log(" - Running in encrypted HTTPS (wss://) mode using: " + argv.cert + ", " + argv.key);
|
||||||
|
webServer = https.createServer({cert: cert, key: key}, http_request);
|
||||||
|
} else {
|
||||||
|
console.log(" - Running in unencrypted HTTP (ws://) mode");
|
||||||
|
webServer = http.createServer(http_request);
|
||||||
|
}
|
||||||
|
webServer.listen(source_port, function() {
|
||||||
|
wsServer = new WebSocketServer({server: webServer,
|
||||||
|
handleProtocols: selectProtocol});
|
||||||
wsServer.on('connection', new_client);
|
wsServer.on('connection', new_client);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attach Flash policyfile answer service
|
// Attach Flash policyfile answer service
|
||||||
policyfile.createServer().listen(-1, httpServer);
|
policyfile.createServer().listen(-1, webServer);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import websockify
|
||||||
|
|
||||||
|
websockify.websocketproxy.websockify_init()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
version = '0.1.0'
|
version = '0.4.1'
|
||||||
name = 'websockify'
|
name = 'websockify'
|
||||||
long_description = open("README.md").read() + "\n" + \
|
long_description = open("README.md").read() + "\n" + \
|
||||||
open("CHANGES.txt").read() + "\n"
|
open("CHANGES.txt").read() + "\n"
|
||||||
@@ -12,19 +12,27 @@ setup(name=name,
|
|||||||
classifiers=[
|
classifiers=[
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
],
|
],
|
||||||
|
data_files=[('share/websockify/include',
|
||||||
|
['include/util.js',
|
||||||
|
'include/base64.js',
|
||||||
|
'include/websock.js']),
|
||||||
|
('share/websockify/include/web-socket-js',
|
||||||
|
['include/web-socket-js/WebSocketMain.swf',
|
||||||
|
'include/web-socket-js/swfobject.js',
|
||||||
|
'include/web-socket-js/web_socket.js'])],
|
||||||
keywords='noVNC websockify',
|
keywords='noVNC websockify',
|
||||||
license='LGPLv3',
|
license='LGPLv3',
|
||||||
url="https://github.com/kanaka/websockify",
|
url="https://github.com/kanaka/websockify",
|
||||||
author="Joel Martin",
|
author="Joel Martin",
|
||||||
author_email="github@martintribe.org",
|
author_email="github@martintribe.org",
|
||||||
|
|
||||||
packages=find_packages(),
|
packages=['websockify'],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=['numpy'],
|
install_requires=['numpy'],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'websockify = websockify:websockify_init',
|
'websockify = websockify.websocketproxy:websockify_init',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
websockify
|
run
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
from websocket import *
|
||||||
|
from websocketproxy import *
|
||||||
@@ -101,7 +101,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
|
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
|
||||||
verbose=False, cert='', key='', ssl_only=None,
|
verbose=False, cert='', key='', ssl_only=None,
|
||||||
daemon=False, record='', web='',
|
daemon=False, record='', web='',
|
||||||
run_once=False, timeout=0):
|
run_once=False, timeout=0, idle_timeout=0):
|
||||||
|
|
||||||
# settings
|
# settings
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
@@ -112,6 +112,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.daemon = daemon
|
self.daemon = daemon
|
||||||
self.run_once = run_once
|
self.run_once = run_once
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
self.idle_timeout = idle_timeout
|
||||||
|
|
||||||
self.launch_time = time.time()
|
self.launch_time = time.time()
|
||||||
self.ws_connection = False
|
self.ws_connection = False
|
||||||
@@ -239,32 +240,33 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
|
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unmask(buf, f):
|
def unmask(buf, hlen, plen):
|
||||||
pstart = f['hlen'] + 4
|
pstart = hlen + 4
|
||||||
pend = pstart + f['length']
|
pend = pstart + plen
|
||||||
if numpy:
|
if numpy:
|
||||||
b = c = s2b('')
|
b = c = s2b('')
|
||||||
if f['length'] >= 4:
|
if plen >= 4:
|
||||||
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
||||||
offset=f['hlen'], count=1)
|
offset=hlen, count=1)
|
||||||
data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
||||||
offset=pstart, count=int(f['length'] / 4))
|
offset=pstart, count=int(plen / 4))
|
||||||
#b = numpy.bitwise_xor(data, mask).data
|
#b = numpy.bitwise_xor(data, mask).data
|
||||||
b = numpy.bitwise_xor(data, mask).tostring()
|
b = numpy.bitwise_xor(data, mask).tostring()
|
||||||
|
|
||||||
if f['length'] % 4:
|
if plen % 4:
|
||||||
#print("Partial unmask")
|
#print("Partial unmask")
|
||||||
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
||||||
offset=f['hlen'], count=(f['length'] % 4))
|
offset=hlen, count=(plen % 4))
|
||||||
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
||||||
offset=pend - (f['length'] % 4),
|
offset=pend - (plen % 4),
|
||||||
count=(f['length'] % 4))
|
count=(plen % 4))
|
||||||
c = numpy.bitwise_xor(data, mask).tostring()
|
c = numpy.bitwise_xor(data, mask).tostring()
|
||||||
return b + c
|
return b + c
|
||||||
else:
|
else:
|
||||||
# Slower fallback
|
# Slower fallback
|
||||||
|
mask = buf[hlen:hlen+4]
|
||||||
data = array.array('B')
|
data = array.array('B')
|
||||||
mask = s2a(f['mask'])
|
mask = s2a(mask)
|
||||||
data.fromstring(buf[pstart:pend])
|
data.fromstring(buf[pstart:pend])
|
||||||
for i in range(len(data)):
|
for i in range(len(data)):
|
||||||
data[i] ^= mask[i % 4]
|
data[i] ^= mask[i % 4]
|
||||||
@@ -303,7 +305,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
Returns:
|
Returns:
|
||||||
{'fin' : 0_or_1,
|
{'fin' : 0_or_1,
|
||||||
'opcode' : number,
|
'opcode' : number,
|
||||||
'mask' : 32_bit_number,
|
'masked' : boolean,
|
||||||
'hlen' : header_bytes_number,
|
'hlen' : header_bytes_number,
|
||||||
'length' : payload_bytes_number,
|
'length' : payload_bytes_number,
|
||||||
'payload' : decoded_buffer,
|
'payload' : decoded_buffer,
|
||||||
@@ -314,7 +316,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
f = {'fin' : 0,
|
f = {'fin' : 0,
|
||||||
'opcode' : 0,
|
'opcode' : 0,
|
||||||
'mask' : 0,
|
'masked' : False,
|
||||||
'hlen' : 2,
|
'hlen' : 2,
|
||||||
'length' : 0,
|
'length' : 0,
|
||||||
'payload' : None,
|
'payload' : None,
|
||||||
@@ -331,7 +333,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
b1, b2 = unpack_from(">BB", buf)
|
b1, b2 = unpack_from(">BB", buf)
|
||||||
f['opcode'] = b1 & 0x0f
|
f['opcode'] = b1 & 0x0f
|
||||||
f['fin'] = (b1 & 0x80) >> 7
|
f['fin'] = (b1 & 0x80) >> 7
|
||||||
has_mask = (b2 & 0x80) >> 7
|
f['masked'] = (b2 & 0x80) >> 7
|
||||||
|
|
||||||
f['length'] = b2 & 0x7f
|
f['length'] = b2 & 0x7f
|
||||||
|
|
||||||
@@ -346,7 +348,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
return f # Incomplete frame header
|
return f # Incomplete frame header
|
||||||
(f['length'],) = unpack_from('>xxQ', buf)
|
(f['length'],) = unpack_from('>xxQ', buf)
|
||||||
|
|
||||||
full_len = f['hlen'] + has_mask * 4 + f['length']
|
full_len = f['hlen'] + f['masked'] * 4 + f['length']
|
||||||
|
|
||||||
if blen < full_len: # Incomplete frame
|
if blen < full_len: # Incomplete frame
|
||||||
return f # Incomplete frame header
|
return f # Incomplete frame header
|
||||||
@@ -355,13 +357,13 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
f['left'] = blen - full_len
|
f['left'] = blen - full_len
|
||||||
|
|
||||||
# Process 1 frame
|
# Process 1 frame
|
||||||
if has_mask:
|
if f['masked']:
|
||||||
# unmask payload
|
# unmask payload
|
||||||
f['mask'] = buf[f['hlen']:f['hlen']+4]
|
f['payload'] = WebSocketServer.unmask(buf, f['hlen'],
|
||||||
f['payload'] = WebSocketServer.unmask(buf, f)
|
f['length'])
|
||||||
else:
|
else:
|
||||||
print("Unmasked frame: %s" % repr(buf))
|
print("Unmasked frame: %s" % repr(buf))
|
||||||
f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len]
|
f['payload'] = buf[(f['hlen'] + f['masked'] * 4):full_len]
|
||||||
|
|
||||||
if base64 and f['opcode'] in [1, 2]:
|
if base64 and f['opcode'] in [1, 2]:
|
||||||
try:
|
try:
|
||||||
@@ -388,6 +390,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
end = buf.find(s2b('\xff'))
|
end = buf.find(s2b('\xff'))
|
||||||
return {'payload': b64decode(buf[1:end]),
|
return {'payload': b64decode(buf[1:end]),
|
||||||
'hlen': 1,
|
'hlen': 1,
|
||||||
|
'masked': False,
|
||||||
'length': end - 1,
|
'length': end - 1,
|
||||||
'left': len(buf) - (end + 1)}
|
'left': len(buf) - (end + 1)}
|
||||||
|
|
||||||
@@ -455,7 +458,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if self.rec:
|
if self.rec:
|
||||||
self.rec.write("%s,\n" %
|
self.rec.write("%s,\n" %
|
||||||
repr("{%s{" % tdelta
|
repr("{%s{" % tdelta
|
||||||
+ encbuf[lenhead:-lentail]))
|
+ encbuf[lenhead:len(encbuf)-lentail]))
|
||||||
|
|
||||||
self.send_parts.append(encbuf)
|
self.send_parts.append(encbuf)
|
||||||
|
|
||||||
@@ -535,8 +538,14 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if self.rec:
|
if self.rec:
|
||||||
start = frame['hlen']
|
start = frame['hlen']
|
||||||
end = frame['hlen'] + frame['length']
|
end = frame['hlen'] + frame['length']
|
||||||
|
if frame['masked']:
|
||||||
|
recbuf = WebSocketServer.unmask(buf, frame['hlen'],
|
||||||
|
frame['length'])
|
||||||
|
else:
|
||||||
|
recbuf = buf[frame['hlen']:frame['hlen'] +
|
||||||
|
frame['length']]
|
||||||
self.rec.write("%s,\n" %
|
self.rec.write("%s,\n" %
|
||||||
repr("}%s}" % tdelta + buf[start:end]))
|
repr("}%s}" % tdelta + recbuf))
|
||||||
|
|
||||||
|
|
||||||
bufs.append(frame['payload'])
|
bufs.append(frame['payload'])
|
||||||
@@ -778,6 +787,10 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.handler_id)
|
self.handler_id)
|
||||||
self.msg("opening record file: %s" % fname)
|
self.msg("opening record file: %s" % fname)
|
||||||
self.rec = open(fname, 'w+')
|
self.rec = open(fname, 'w+')
|
||||||
|
encoding = "binary"
|
||||||
|
if self.base64: encoding = "base64"
|
||||||
|
self.rec.write("var VNC_frame_encoding = '%s';\n"
|
||||||
|
% encoding)
|
||||||
self.rec.write("var VNC_frame_data = [\n")
|
self.rec.write("var VNC_frame_data = [\n")
|
||||||
|
|
||||||
self.ws_connection = True
|
self.ws_connection = True
|
||||||
@@ -799,7 +812,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.msg(traceback.format_exc())
|
self.msg(traceback.format_exc())
|
||||||
finally:
|
finally:
|
||||||
if self.rec:
|
if self.rec:
|
||||||
self.rec.write("'EOF']\n")
|
self.rec.write("'EOF'];\n")
|
||||||
self.rec.close()
|
self.rec.close()
|
||||||
|
|
||||||
if self.client and self.client != startsock:
|
if self.client and self.client != startsock:
|
||||||
@@ -831,12 +844,17 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
# os.fork() (python 2.4) child reaper
|
# os.fork() (python 2.4) child reaper
|
||||||
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
|
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
|
||||||
|
|
||||||
|
last_active_time = self.launch_time
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self.client = None
|
self.client = None
|
||||||
startsock = None
|
startsock = None
|
||||||
pid = err = 0
|
pid = err = 0
|
||||||
|
child_count = 0
|
||||||
|
|
||||||
|
if multiprocessing and self.idle_timeout:
|
||||||
|
child_count = len(multiprocessing.active_children())
|
||||||
|
|
||||||
time_elapsed = time.time() - self.launch_time
|
time_elapsed = time.time() - self.launch_time
|
||||||
if self.timeout and time_elapsed > self.timeout:
|
if self.timeout and time_elapsed > self.timeout:
|
||||||
@@ -844,6 +862,19 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
% self.timeout)
|
% self.timeout)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if self.idle_timeout:
|
||||||
|
idle_time = 0
|
||||||
|
if child_count == 0:
|
||||||
|
idle_time = time.time() - last_active_time
|
||||||
|
else:
|
||||||
|
idle_time = 0
|
||||||
|
last_active_time = time.time()
|
||||||
|
|
||||||
|
if idle_time > self.idle_timeout and child_count == 0:
|
||||||
|
self.msg('listener exit due to --idle-timeout %s'
|
||||||
|
% self.idle_timeout)
|
||||||
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.poll()
|
self.poll()
|
||||||
|
|
||||||
@@ -910,6 +941,11 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if startsock:
|
if startsock:
|
||||||
startsock.close()
|
startsock.close()
|
||||||
|
|
||||||
|
# Close listen port
|
||||||
|
self.vmsg("Closing socket listening at %s:%s"
|
||||||
|
% (self.listen_host, self.listen_port))
|
||||||
|
lsock.close()
|
||||||
|
|
||||||
|
|
||||||
# HTTP handler with WebSocket upgrade support
|
# HTTP handler with WebSocket upgrade support
|
||||||
class WSRequestHandler(SimpleHTTPRequestHandler):
|
class WSRequestHandler(SimpleHTTPRequestHandler):
|
||||||
@@ -11,11 +11,14 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import socket, optparse, time, os, sys, subprocess
|
import signal, socket, optparse, time, os, sys, subprocess
|
||||||
from select import select
|
from select import select
|
||||||
import websocket
|
import websocket
|
||||||
try: from urllib.parse import parse_qs, urlparse
|
try:
|
||||||
except: from urlparse import parse_qs, urlparse
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
except:
|
||||||
|
from cgi import parse_qs
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
class WebSocketProxy(websocket.WebSocketServer):
|
class WebSocketProxy(websocket.WebSocketServer):
|
||||||
"""
|
"""
|
||||||
@@ -41,13 +44,13 @@ Traffic Legend:
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Save off proxy specific options
|
# Save off proxy specific options
|
||||||
self.target_host = kwargs.pop('target_host')
|
self.target_host = kwargs.pop('target_host', None)
|
||||||
self.target_port = kwargs.pop('target_port')
|
self.target_port = kwargs.pop('target_port', None)
|
||||||
self.wrap_cmd = kwargs.pop('wrap_cmd')
|
self.wrap_cmd = kwargs.pop('wrap_cmd', None)
|
||||||
self.wrap_mode = kwargs.pop('wrap_mode')
|
self.wrap_mode = kwargs.pop('wrap_mode', None)
|
||||||
self.unix_target = kwargs.pop('unix_target')
|
self.unix_target = kwargs.pop('unix_target', None)
|
||||||
self.ssl_target = kwargs.pop('ssl_target')
|
self.ssl_target = kwargs.pop('ssl_target', None)
|
||||||
self.target_cfg = kwargs.pop('target_cfg')
|
self.target_cfg = kwargs.pop('target_cfg', None)
|
||||||
# Last 3 timestamps command was run
|
# Last 3 timestamps command was run
|
||||||
self.wrap_times = [0, 0, 0]
|
self.wrap_times = [0, 0, 0]
|
||||||
|
|
||||||
@@ -87,7 +90,7 @@ Traffic Legend:
|
|||||||
self.wrap_times.append(time.time())
|
self.wrap_times.append(time.time())
|
||||||
self.wrap_times.pop(0)
|
self.wrap_times.pop(0)
|
||||||
self.cmd = subprocess.Popen(
|
self.cmd = subprocess.Popen(
|
||||||
self.wrap_cmd, env=os.environ)
|
self.wrap_cmd, env=os.environ, preexec_fn=_subprocess_setup)
|
||||||
self.spawn_message = True
|
self.spawn_message = True
|
||||||
|
|
||||||
def started(self):
|
def started(self):
|
||||||
@@ -144,7 +147,7 @@ Traffic Legend:
|
|||||||
else:
|
else:
|
||||||
self.run_wrap_cmd()
|
self.run_wrap_cmd()
|
||||||
|
|
||||||
#
|
#
|
||||||
# Routines above this point are run in the master listener
|
# Routines above this point are run in the master listener
|
||||||
# process.
|
# process.
|
||||||
#
|
#
|
||||||
@@ -165,13 +168,13 @@ Traffic Legend:
|
|||||||
|
|
||||||
# Connect to the target
|
# Connect to the target
|
||||||
if self.wrap_cmd:
|
if self.wrap_cmd:
|
||||||
msg = "connecting to command: %s" % (" ".join(self.wrap_cmd), self.target_port)
|
msg = "connecting to command: '%s' (port %s)" % (" ".join(self.wrap_cmd), self.target_port)
|
||||||
elif self.unix_target:
|
elif self.unix_target:
|
||||||
msg = "connecting to unix socket: %s" % self.unix_target
|
msg = "connecting to unix socket: %s" % self.unix_target
|
||||||
else:
|
else:
|
||||||
msg = "connecting to: %s:%s" % (
|
msg = "connecting to: %s:%s" % (
|
||||||
self.target_host, self.target_port)
|
self.target_host, self.target_port)
|
||||||
|
|
||||||
if self.ssl_target:
|
if self.ssl_target:
|
||||||
msg += " (using SSL)"
|
msg += " (using SSL)"
|
||||||
self.msg(msg)
|
self.msg(msg)
|
||||||
@@ -205,7 +208,7 @@ Traffic Legend:
|
|||||||
# Extract the token parameter from url
|
# Extract the token parameter from url
|
||||||
args = parse_qs(urlparse(path)[4]) # 4 is the query from url
|
args = parse_qs(urlparse(path)[4]) # 4 is the query from url
|
||||||
|
|
||||||
if not len(args['token']):
|
if not args.has_key('token') or not len(args['token']):
|
||||||
raise self.EClose("Token not present")
|
raise self.EClose("Token not present")
|
||||||
|
|
||||||
token = args['token'][0].rstrip('\n')
|
token = args['token'][0].rstrip('\n')
|
||||||
@@ -249,6 +252,24 @@ Traffic Legend:
|
|||||||
ins, outs, excepts = select(rlist, wlist, [], 1)
|
ins, outs, excepts = select(rlist, wlist, [], 1)
|
||||||
if excepts: raise Exception("Socket exception")
|
if excepts: raise Exception("Socket exception")
|
||||||
|
|
||||||
|
if self.client in outs:
|
||||||
|
# Send queued target data to the client
|
||||||
|
c_pend = self.send_frames(cqueue)
|
||||||
|
|
||||||
|
cqueue = []
|
||||||
|
|
||||||
|
if self.client in ins:
|
||||||
|
# Receive client data, decode it, and queue for target
|
||||||
|
bufs, closed = self.recv_frames()
|
||||||
|
tqueue.extend(bufs)
|
||||||
|
|
||||||
|
if closed:
|
||||||
|
# TODO: What about blocking on client socket?
|
||||||
|
self.vmsg("%s:%s: Client closed connection" %(
|
||||||
|
self.target_host, self.target_port))
|
||||||
|
raise self.CClose(closed['code'], closed['reason'])
|
||||||
|
|
||||||
|
|
||||||
if target in outs:
|
if target in outs:
|
||||||
# Send queued client data to the target
|
# Send queued client data to the target
|
||||||
dat = tqueue.pop(0)
|
dat = tqueue.pop(0)
|
||||||
@@ -273,24 +294,13 @@ Traffic Legend:
|
|||||||
self.traffic("{")
|
self.traffic("{")
|
||||||
|
|
||||||
|
|
||||||
if self.client in outs:
|
|
||||||
# Send queued target data to the client
|
|
||||||
c_pend = self.send_frames(cqueue)
|
|
||||||
|
|
||||||
cqueue = []
|
def _subprocess_setup():
|
||||||
|
# Python installs a SIGPIPE handler by default. This is usually not what
|
||||||
|
# non-Python successfulbprocesses expect.
|
||||||
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||||
|
|
||||||
|
|
||||||
if self.client in ins:
|
|
||||||
# Receive client data, decode it, and queue for target
|
|
||||||
bufs, closed = self.recv_frames()
|
|
||||||
tqueue.extend(bufs)
|
|
||||||
|
|
||||||
if closed:
|
|
||||||
# TODO: What about blocking on client socket?
|
|
||||||
self.vmsg("%s:%s: Client closed connection" %(
|
|
||||||
self.target_host, self.target_port))
|
|
||||||
raise self.CClose(closed['code'], closed['reason'])
|
|
||||||
|
|
||||||
def websockify_init():
|
def websockify_init():
|
||||||
usage = "\n %prog [options]"
|
usage = "\n %prog [options]"
|
||||||
usage += " [source_addr:]source_port [target_addr:target_port]"
|
usage += " [source_addr:]source_port [target_addr:target_port]"
|
||||||
@@ -308,6 +318,9 @@ def websockify_init():
|
|||||||
help="handle a single WebSocket connection and exit")
|
help="handle a single WebSocket connection and exit")
|
||||||
parser.add_option("--timeout", type=int, default=0,
|
parser.add_option("--timeout", type=int, default=0,
|
||||||
help="after TIMEOUT seconds exit when not connected")
|
help="after TIMEOUT seconds exit when not connected")
|
||||||
|
parser.add_option("--idle-timeout", type=int, default=0,
|
||||||
|
help="server exits after TIMEOUT seconds if there are no "
|
||||||
|
"active connections")
|
||||||
parser.add_option("--cert", default="self.pem",
|
parser.add_option("--cert", default="self.pem",
|
||||||
help="SSL certificate file")
|
help="SSL certificate file")
|
||||||
parser.add_option("--key", default=None,
|
parser.add_option("--key", default=None,
|
||||||
@@ -335,7 +348,7 @@ def websockify_init():
|
|||||||
(opts, args) = parser.parse_args()
|
(opts, args) = parser.parse_args()
|
||||||
|
|
||||||
# Sanity checks
|
# Sanity checks
|
||||||
if len(args) < 2 and not opts.target_cfg:
|
if len(args) < 2 and not (opts.target_cfg or opts.unix_target):
|
||||||
parser.error("Too few arguments")
|
parser.error("Too few arguments")
|
||||||
if sys.argv.count('--'):
|
if sys.argv.count('--'):
|
||||||
opts.wrap_cmd = args[1:]
|
opts.wrap_cmd = args[1:]
|
||||||
@@ -346,7 +359,7 @@ def websockify_init():
|
|||||||
|
|
||||||
if not websocket.ssl and opts.ssl_target:
|
if not websocket.ssl and opts.ssl_target:
|
||||||
parser.error("SSL target requested and Python SSL module not loaded.");
|
parser.error("SSL target requested and Python SSL module not loaded.");
|
||||||
|
|
||||||
if opts.ssl_only and not os.path.exists(opts.cert):
|
if opts.ssl_only and not os.path.exists(opts.cert):
|
||||||
parser.error("SSL only and %s not found" % opts.cert)
|
parser.error("SSL only and %s not found" % opts.cert)
|
||||||
|
|
||||||
Reference in New Issue
Block a user