├── .gitignore
├── .pyup.yml
├── .travis.yml
├── LICENSE
├── LICENSE.md
├── README.rst
├── example
├── app.yaml
├── deploy.sh
├── run-thread.py
├── run.py
└── static
│ ├── index.html
│ └── just-grid.css
├── requirements.test.txt
├── run-tests
├── setup.py
├── tests
├── __init__.py
├── async.py
├── sync.py
├── test_async.py
├── test_handler.py
├── test_js_static.py
├── test_log_thread_exc.py
├── test_ping.py
└── test_sync.py
└── wsrpc
├── __init__.py
├── static
├── q.js
├── q.min.js
├── wsrpc.js
└── wsrpc.min.js
└── websocket
├── __init__.py
├── common.py
├── handler.py
├── route.py
└── tools.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env*/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | lib/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | *.egg-info/
22 | .installed.cfg
23 | *.egg
24 |
25 | # PyInstaller
26 | # Usually these files are written by a python script from a template
27 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
28 | *.manifest
29 | *.spec
30 |
31 | # Installer logs
32 | pip-log.txt
33 | pip-delete-this-directory.txt
34 |
35 | # Unit test / coverage reports
36 | htmlcov/
37 | .tox/
38 | .coverage
39 | .cache
40 | nosetests.xml
41 | coverage.xml
42 |
43 | # Translations
44 | *.mo
45 | *.pot
46 |
47 | # Django stuff:
48 | *.log
49 |
50 | # Sphinx documentation
51 | docs/_build/
52 |
53 | # PyBuilder
54 | target/
55 |
--------------------------------------------------------------------------------
/.pyup.yml:
--------------------------------------------------------------------------------
1 | # autogenerated pyup.io config file
2 | # see https://pyup.io/docs/configuration/ for all available options
3 |
4 | update: insecure
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.6"
4 | - "2.7"
5 | - "3.2"
6 | - "3.4"
7 | - "3.5.0b3"
8 | - "3.5-dev"
9 | # command to install dependencies
10 | install: "pip install -e ."
11 | before_script:
12 | - pip install -r requirements.test.txt
13 | script: ./run-tests
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | LICENSE.md
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | ==============
3 |
4 | _Version 2.0, January 2004_
5 | _<>_
6 |
7 | ### Terms and Conditions for use, reproduction, and distribution
8 |
9 | #### 1. Definitions
10 |
11 | “License” shall mean the terms and conditions for use, reproduction, and
12 | distribution as defined by Sections 1 through 9 of this document.
13 |
14 | “Licensor” shall mean the copyright owner or entity authorized by the copyright
15 | owner that is granting the License.
16 |
17 | “Legal Entity” shall mean the union of the acting entity and all other entities
18 | that control, are controlled by, or are under common control with that entity.
19 | For the purposes of this definition, “control” means **(i)** the power, direct or
20 | indirect, to cause the direction or management of such entity, whether by
21 | contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
22 | outstanding shares, or **(iii)** beneficial ownership of such entity.
23 |
24 | “You” (or “Your”) shall mean an individual or Legal Entity exercising
25 | permissions granted by this License.
26 |
27 | “Source” form shall mean the preferred form for making modifications, including
28 | but not limited to software source code, documentation source, and configuration
29 | files.
30 |
31 | “Object” form shall mean any form resulting from mechanical transformation or
32 | translation of a Source form, including but not limited to compiled object code,
33 | generated documentation, and conversions to other media types.
34 |
35 | “Work” shall mean the work of authorship, whether in Source or Object form, made
36 | available under the License, as indicated by a copyright notice that is included
37 | in or attached to the work (an example is provided in the Appendix below).
38 |
39 | “Derivative Works” shall mean any work, whether in Source or Object form, that
40 | is based on (or derived from) the Work and for which the editorial revisions,
41 | annotations, elaborations, or other modifications represent, as a whole, an
42 | original work of authorship. For the purposes of this License, Derivative Works
43 | shall not include works that remain separable from, or merely link (or bind by
44 | name) to the interfaces of, the Work and Derivative Works thereof.
45 |
46 | “Contribution” shall mean any work of authorship, including the original version
47 | of the Work and any modifications or additions to that Work or Derivative Works
48 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
49 | by the copyright owner or by an individual or Legal Entity authorized to submit
50 | on behalf of the copyright owner. For the purposes of this definition,
51 | “submitted” means any form of electronic, verbal, or written communication sent
52 | to the Licensor or its representatives, including but not limited to
53 | communication on electronic mailing lists, source code control systems, and
54 | issue tracking systems that are managed by, or on behalf of, the Licensor for
55 | the purpose of discussing and improving the Work, but excluding communication
56 | that is conspicuously marked or otherwise designated in writing by the copyright
57 | owner as “Not a Contribution.”
58 |
59 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf
60 | of whom a Contribution has been received by Licensor and subsequently
61 | incorporated within the Work.
62 |
63 | #### 2. Grant of Copyright License
64 |
65 | Subject to the terms and conditions of this License, each Contributor hereby
66 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
67 | irrevocable copyright license to reproduce, prepare Derivative Works of,
68 | publicly display, publicly perform, sublicense, and distribute the Work and such
69 | Derivative Works in Source or Object form.
70 |
71 | #### 3. Grant of Patent License
72 |
73 | Subject to the terms and conditions of this License, each Contributor hereby
74 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
75 | irrevocable (except as stated in this section) patent license to make, have
76 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
77 | such license applies only to those patent claims licensable by such Contributor
78 | that are necessarily infringed by their Contribution(s) alone or by combination
79 | of their Contribution(s) with the Work to which such Contribution(s) was
80 | submitted. If You institute patent litigation against any entity (including a
81 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
82 | Contribution incorporated within the Work constitutes direct or contributory
83 | patent infringement, then any patent licenses granted to You under this License
84 | for that Work shall terminate as of the date such litigation is filed.
85 |
86 | #### 4. Redistribution
87 |
88 | You may reproduce and distribute copies of the Work or Derivative Works thereof
89 | in any medium, with or without modifications, and in Source or Object form,
90 | provided that You meet the following conditions:
91 |
92 | * **(a)** You must give any other recipients of the Work or Derivative Works a copy of
93 | this License; and
94 | * **(b)** You must cause any modified files to carry prominent notices stating that You
95 | changed the files; and
96 | * **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
97 | all copyright, patent, trademark, and attribution notices from the Source form
98 | of the Work, excluding those notices that do not pertain to any part of the
99 | Derivative Works; and
100 | * **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
101 | Derivative Works that You distribute must include a readable copy of the
102 | attribution notices contained within such NOTICE file, excluding those notices
103 | that do not pertain to any part of the Derivative Works, in at least one of the
104 | following places: within a NOTICE text file distributed as part of the
105 | Derivative Works; within the Source form or documentation, if provided along
106 | with the Derivative Works; or, within a display generated by the Derivative
107 | Works, if and wherever such third-party notices normally appear. The contents of
108 | the NOTICE file are for informational purposes only and do not modify the
109 | License. You may add Your own attribution notices within Derivative Works that
110 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
111 | provided that such additional attribution notices cannot be construed as
112 | modifying the License.
113 |
114 | You may add Your own copyright statement to Your modifications and may provide
115 | additional or different license terms and conditions for use, reproduction, or
116 | distribution of Your modifications, or for any such Derivative Works as a whole,
117 | provided Your use, reproduction, and distribution of the Work otherwise complies
118 | with the conditions stated in this License.
119 |
120 | #### 5. Submission of Contributions
121 |
122 | Unless You explicitly state otherwise, any Contribution intentionally submitted
123 | for inclusion in the Work by You to the Licensor shall be under the terms and
124 | conditions of this License, without any additional terms or conditions.
125 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
126 | any separate license agreement you may have executed with Licensor regarding
127 | such Contributions.
128 |
129 | #### 6. Trademarks
130 |
131 | This License does not grant permission to use the trade names, trademarks,
132 | service marks, or product names of the Licensor, except as required for
133 | reasonable and customary use in describing the origin of the Work and
134 | reproducing the content of the NOTICE file.
135 |
136 | #### 7. Disclaimer of Warranty
137 |
138 | Unless required by applicable law or agreed to in writing, Licensor provides the
139 | Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
140 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
141 | including, without limitation, any warranties or conditions of TITLE,
142 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
143 | solely responsible for determining the appropriateness of using or
144 | redistributing the Work and assume any risks associated with Your exercise of
145 | permissions under this License.
146 |
147 | #### 8. Limitation of Liability
148 |
149 | In no event and under no legal theory, whether in tort (including negligence),
150 | contract, or otherwise, unless required by applicable law (such as deliberate
151 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
152 | liable to You for damages, including any direct, indirect, special, incidental,
153 | or consequential damages of any character arising as a result of this License or
154 | out of the use or inability to use the Work (including but not limited to
155 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
156 | any and all other commercial damages or losses), even if such Contributor has
157 | been advised of the possibility of such damages.
158 |
159 | #### 9. Accepting Warranty or Additional Liability
160 |
161 | While redistributing the Work or Derivative Works thereof, You may choose to
162 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
163 | other liability obligations and/or rights consistent with this License. However,
164 | in accepting such obligations, You may act only on Your own behalf and on Your
165 | sole responsibility, not on behalf of any other Contributor, and only if You
166 | agree to indemnify, defend, and hold each Contributor harmless for any liability
167 | incurred by, or claims asserted against, such Contributor by reason of your
168 | accepting any such warranty or additional liability.
169 |
170 | _END OF TERMS AND CONDITIONS_
171 |
172 | ### APPENDIX: How to apply the Apache License to your work
173 |
174 | To apply the Apache License to your work, attach the following boilerplate
175 | notice, with the fields enclosed by brackets `[]` replaced with your own
176 | identifying information. (Don't include the brackets!) The text should be
177 | enclosed in the appropriate comment syntax for the file format. We also
178 | recommend that a file or class name and description of purpose be included on
179 | the same “printed page” as the copyright notice for easier identification within
180 | third-party archives.
181 |
182 | Copyright [yyyy] [name of copyright owner]
183 |
184 | Licensed under the Apache License, Version 2.0 (the "License");
185 | you may not use this file except in compliance with the License.
186 | You may obtain a copy of the License at
187 |
188 | http://www.apache.org/licenses/LICENSE-2.0
189 |
190 | Unless required by applicable law or agreed to in writing, software
191 | distributed under the License is distributed on an "AS IS" BASIS,
192 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
193 | See the License for the specific language governing permissions and
194 | limitations under the License.
195 |
196 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | WSRPC Tornado
2 | =============
3 |
4 | .. image:: https://travis-ci.org/wsrpc/wsrpc-tornado.svg
5 | :target: https://travis-ci.org/wsrpc/wsrpc-tornado
6 | :alt: Travis CI
7 |
8 | .. image:: https://img.shields.io/pypi/v/wsrpc-tornado.svg
9 | :target: https://pypi.python.org/pypi/wsrpc-tornado/
10 | :alt: Latest Version
11 |
12 | .. image:: https://img.shields.io/pypi/wheel/wsrpc-tornado.svg
13 | :target: https://pypi.python.org/pypi/wsrpc-tornado/
14 |
15 | .. image:: https://img.shields.io/pypi/pyversions/wsrpc-tornado.svg
16 | :target: https://pypi.python.org/pypi/wsrpc-tornado/
17 |
18 | .. image:: https://img.shields.io/pypi/l/wsrpc-tornado.svg
19 | :target: https://pypi.python.org/pypi/wsrpc-tornado/
20 |
21 | Easy to use minimal WebSocket Remote Procedure Call library for tornado
22 | servers. See online demo_.
23 |
24 | Also, there is `aiohttp WSRPC`_ implementation.
25 |
26 | Features
27 | --------
28 |
29 | * Initiating call client function from server side.
30 | * Calling the server method from the client.
31 | * Transferring any exceptions from a client side to the server side and vise versa.
32 | * The frontend-library are well done for usage without any modification.
33 | * Fully asynchronous server-side functions.
34 | * Thread-based websocket handler for writing fully-synchronous code (for synchronous database drivers etc.)
35 | * Protected server-side methods (starts with underline never will be call from clients-side directly)
36 | * Asynchronous connection protocol. Server or client can call multiple methods with unpredictable ordering of answers.
37 |
38 |
39 | Installation
40 | ------------
41 |
42 | Install via pip::
43 |
44 | pip install wsrpc-tornado
45 |
46 |
47 | Install ujson if you want::
48 |
49 | pip install ujson
50 |
51 |
52 |
53 | Simple usage
54 | ------------
55 |
56 | Add the backend side
57 |
58 |
59 | .. code-block:: python
60 |
61 | from time import time
62 | ## If you want write async tornado code import it
63 | # from from wsrpc import WebSocketRoute, WebSocket, wsrpc_static
64 | ## else you should use thread-base handler
65 | from wsrpc import WebSocketRoute, WebSocketThreaded as WebSocket, wsrpc_static
66 |
67 | tornado.web.Application((
68 | # js static files will available as "/js/wsrpc.min.js".
69 | wsrpc_static(r'/js/(.*)'),
70 | # WebSocket handler. Client will connect here.
71 | (r"/ws/", WebSocket),
72 | # Serve other static files
73 | (r'/(.*)', tornado.web.StaticFileHandler, {
74 | 'path': os.path.join(project_root, 'static'),
75 | 'default_filename': 'index.html'
76 | }),
77 | ))
78 |
79 | # This class should be call by client.
80 | # Connection object will be have the instance of this class when will call route-alias.
81 | class TestRoute(WebSocketRoute):
82 | # This method will be executed when client will call route-alias first time.
83 | def init(self, **kwargs):
84 | # the python __init__ must be return "self". This method might return anything.
85 | return kwargs
86 |
87 | def getEpoch(self):
88 | # this method named by camelCase because the client can call it.
89 | return time()
90 |
91 | # stateful request
92 | # this is the route alias TestRoute as "test1"
93 | WebSocket.ROUTES['test1'] = TestRoute
94 |
95 | # stateless request
96 | WebSocket.ROUTES['test2'] = lambda *a, **kw: True
97 |
98 | # initialize ThreadPool. Needed when using WebSocketThreaded.
99 | WebSocket.init_pool()
100 |
101 |
102 |
103 | Add the frontend side
104 |
105 |
106 | .. code-block:: HTML
107 |
108 |
109 |
110 |
124 |
125 | Reverse call from Server to Client
126 | ----------------------------------
127 | backend:
128 |
129 | .. code-block:: python
130 |
131 | def do_notify(self):
132 | awesome = 'Notification for you!'
133 | yield self.socket.call('notify', result=awesome)
134 |
135 | frontend:
136 |
137 | .. code-block:: HTML
138 |
139 |
145 |
146 | .. _demo: https://demo.wsrpc.info/
147 |
148 | .. _aiohttp WSRPC: https://github.com/wsrpc/wsrpc-aiohttp
149 |
--------------------------------------------------------------------------------
/example/app.yaml:
--------------------------------------------------------------------------------
1 | application: wsrpc-example
2 | version: 2
3 | runtime: python27
4 | api_version: 1
5 | threadsafe: yes
6 |
7 | handlers:
8 | - url: /static/
9 | static_dir: static
10 |
11 | - url: /.*
12 | script: run.application
--------------------------------------------------------------------------------
/example/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | virtualenv env
4 |
5 | ./env/bin/pip install ..
6 |
7 | virtualenv --relocatable env
8 |
9 | mkdir -p lib
10 |
11 | rsync -av --delete --delete-excluded\
12 | --exclude="*.dist-info" \
13 | --exclude="*.egg-info" \
14 | --exclude="*.pyc" \
15 | --exclude="_*" \
16 | --exclude="pip" \
17 | --exclude="setuptools" \
18 | --exclude="wheel" \
19 | --exclude="pkg_resources" \
20 | --exclude="*.so" \
21 | --exclude="*.dll" \
22 | --include="*.py" \
23 | env/lib/python*/site-packages/ lib
24 |
25 | cd lib
26 |
27 | for package in *;
28 | do zip -r ${package}.zip ${package} && rm -fr ${package}
29 | done
30 |
31 | cd -
32 |
33 | rm -fr env
--------------------------------------------------------------------------------
/example/run-thread.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 | import os
4 | import uuid
5 | import tornado.web
6 | import tornado.httpserver
7 | import tornado.ioloop
8 | import tornado.gen
9 |
10 | from random import randint
11 | from time import sleep, time
12 |
13 | from tornado.log import app_log as log
14 | from tornado.options import define, options
15 |
16 | from wsrpc import WebSocketRoute, WebSocketThreaded as WebSocket, wsrpc_static
17 |
18 | define("listen", default='127.0.0.1', help="run on the given host")
19 | define("port", default=9090, help="run on the given port", type=int)
20 | define("pool_size", default=50, help="Default threadpool size", type=int)
21 | define("debug", default=False, help="Debug", type=bool)
22 | define("gzip", default=True, help="GZIP responses", type=bool)
23 |
24 | COOKIE_SECRET = str(uuid.uuid4())
25 |
26 |
27 | class Application(tornado.web.Application):
28 |
29 | def __init__(self):
30 | project_root = os.path.dirname(os.path.abspath(__file__))
31 | handlers = (
32 | wsrpc_static(r'/js/(.*)'),
33 | (r"/ws/", WebSocket),
34 | (r'/(.*)', tornado.web.StaticFileHandler, {
35 | 'path': os.path.join(project_root, 'static'),
36 | 'default_filename': 'index.html'
37 | }),
38 | )
39 |
40 | tornado.web.Application.__init__(
41 | self,
42 | handlers,
43 | xsrf_cookies=False,
44 | cookie_secret=COOKIE_SECRET,
45 | debug=options.debug,
46 | reload=options.debug,
47 | gzip=options.gzip,
48 | )
49 |
50 |
51 | class TestRoute(WebSocketRoute):
52 | JOKES = [
53 | '[ $[ $RANDOM % 6 ] == 0 ] && rm -rf / || echo *Click*',
54 | 'It’s always a long day, 86,400 won’t fit into a short.',
55 | 'Programming is like sex:\nOne mistake and you have to support it for the rest of your life.',
56 | 'There are three kinds of lies: lies, damned lies, and benchmarks.',
57 | 'The generation of random numbers is too important to be left to chance.',
58 | 'A SQL query goes to a restaurant, walks up to 2 tables and says “Can I join you”?',
59 | ]
60 |
61 | def init(self, **kwargs):
62 | return kwargs
63 |
64 | def delayed(self, delay=0):
65 | sleep(delay)
66 | return "I'm delayed {0} seconds".format(delay)
67 |
68 | def getEpoch(self):
69 | return time()
70 |
71 | def requiredArgument(self, myarg):
72 | return True
73 |
74 | def _secure_method(self):
75 | return 'WTF???'
76 |
77 | def getJoke(self):
78 | joke = self.JOKES[randint(0, len(self.JOKES) - 1)]
79 | self.socket.call('joke', joke=joke, callback=self._joke_result)
80 | return "Ok."
81 |
82 | def _joke_result(self, result):
83 | log.info('Client said that was "{0}"'.format('awesome' if result else 'awful'))
84 | self.socket.call('print', result='Cool' if result else 'Hmm.. Try again.')
85 |
86 |
87 | WebSocket.ROUTES['test'] = TestRoute
88 |
89 |
90 | def main():
91 | tornado.options.parse_command_line()
92 | WebSocket.init_pool()
93 | http_server = tornado.httpserver.HTTPServer(Application())
94 | http_server.listen(options.port, address=options.listen)
95 | log.info('Server started {host}:{port}'.format(host=options.listen, port=options.port))
96 | tornado.ioloop.IOLoop.instance().start()
97 |
98 |
99 | if __name__ == "__main__":
100 | exit(main())
101 |
--------------------------------------------------------------------------------
/example/run.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 | import os
4 | import uuid
5 | import tornado.web
6 | import tornado.httpserver
7 | import tornado.ioloop
8 | import tornado.gen
9 | import tornado.wsgi
10 |
11 | from random import randint
12 | from time import sleep, time
13 |
14 | from tornado.log import app_log as log
15 | from tornado.options import define, options
16 |
17 | from wsrpc import WebSocketRoute, WebSocket, wsrpc_static
18 |
19 | define("listen", default='127.0.0.1', help="run on the given host")
20 | define("port", default=9090, help="run on the given port", type=int)
21 | define("pool_size", default=50, help="Default threadpool size", type=int)
22 | define("debug", default=False, help="Debug", type=bool)
23 | define("gzip", default=True, help="GZIP responses", type=bool)
24 |
25 | COOKIE_SECRET = str(uuid.uuid4())
26 |
27 |
28 | class Application(tornado.web.Application):
29 | def __init__(self):
30 | project_root = os.path.dirname(os.path.abspath(__file__))
31 | handlers = (
32 | wsrpc_static(r'/js/(.*)'),
33 | (r"/ws/", WebSocket),
34 | (r'/(.*)', tornado.web.StaticFileHandler, {
35 | 'path': os.path.join(project_root, 'static'),
36 | 'default_filename': 'index.html'
37 | }),
38 | )
39 |
40 | tornado.web.Application.__init__(
41 | self,
42 | handlers,
43 | xsrf_cookies=False,
44 | cookie_secret=COOKIE_SECRET,
45 | debug=options.debug,
46 | reload=options.debug,
47 | gzip=options.gzip,
48 | )
49 |
50 |
51 | class TestRoute(WebSocketRoute):
52 | JOKES = [
53 | '[ $[ $RANDOM % 6 ] == 0 ] && rm -rf / || echo *Click*',
54 | 'It’s always a long day, 86,400 won’t fit into a short.',
55 | 'Programming is like sex:\nOne mistake and you have to support it for the rest of your life.',
56 | 'There are three kinds of lies: lies, damned lies, and benchmarks.',
57 | 'The generation of random numbers is too important to be left to chance.',
58 | 'A SQL query goes to a restaurant, walks up to 2 tables and says “Can I join you”?',
59 | ]
60 |
61 | def init(self, **kwargs):
62 | return kwargs
63 |
64 | @tornado.gen.coroutine
65 | def delayed(self, delay=0):
66 | future = tornado.gen.Future()
67 | tornado.ioloop.IOLoop.instance().call_later(delay, lambda: future.set_result(True))
68 | yield future
69 | raise tornado.gen.Return("I'm delayed {0} seconds".format(delay))
70 |
71 | def getEpoch(self):
72 | return time()
73 |
74 | def requiredArgument(self, myarg):
75 | return True
76 |
77 | def _secure_method(self):
78 | return 'WTF???'
79 |
80 | def exc(self):
81 | raise Exception(u"Test Тест テスト 测试")
82 |
83 | @tornado.gen.coroutine
84 | def getJoke(self):
85 | joke = self.JOKES[randint(0, len(self.JOKES) - 1)]
86 | result = yield self.socket.call('joke', joke=joke)
87 | log.info('Client said that was "{0}"'.format('awesome' if result else 'awful'))
88 | yield self.socket.call('print', result='Cool' if result else 'Hmm.. Try again.')
89 | raise tornado.gen.Return("Ok.")
90 |
91 |
92 | WebSocket.ROUTES['test'] = TestRoute
93 |
94 |
95 | def main():
96 | tornado.options.parse_command_line()
97 | http_server = tornado.httpserver.HTTPServer(Application())
98 | http_server.listen(options.port, address=options.listen)
99 | log.info('Server started {host}:{port}'.format(host=options.listen, port=options.port))
100 | tornado.ioloop.IOLoop.instance().start()
101 |
102 |
103 | if __name__ == "__main__":
104 | exit(main())
105 | else:
106 | try:
107 | from google.appengine.ext import vendor
108 | vendor.add('lib')
109 |
110 | except ImportError:
111 | pass
112 |
113 | application = tornado.wsgi.WSGIAdapter(Application())
114 |
--------------------------------------------------------------------------------
/example/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |