Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | # Copyright 2011, Google Inc. |
michael@0 | 2 | # All rights reserved. |
michael@0 | 3 | # |
michael@0 | 4 | # Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | # modification, are permitted provided that the following conditions are |
michael@0 | 6 | # met: |
michael@0 | 7 | # |
michael@0 | 8 | # * Redistributions of source code must retain the above copyright |
michael@0 | 9 | # notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | # * Redistributions in binary form must reproduce the above |
michael@0 | 11 | # copyright notice, this list of conditions and the following disclaimer |
michael@0 | 12 | # in the documentation and/or other materials provided with the |
michael@0 | 13 | # distribution. |
michael@0 | 14 | # * Neither the name of Google Inc. nor the names of its |
michael@0 | 15 | # contributors may be used to endorse or promote products derived from |
michael@0 | 16 | # this software without specific prior written permission. |
michael@0 | 17 | # |
michael@0 | 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 29 | |
michael@0 | 30 | |
michael@0 | 31 | """WebSocket extension for Apache HTTP Server. |
michael@0 | 32 | |
michael@0 | 33 | mod_pywebsocket is a WebSocket extension for Apache HTTP Server |
michael@0 | 34 | intended for testing or experimental purposes. mod_python is required. |
michael@0 | 35 | |
michael@0 | 36 | |
michael@0 | 37 | Installation: |
michael@0 | 38 | |
michael@0 | 39 | 0. Prepare an Apache HTTP Server for which mod_python is enabled. |
michael@0 | 40 | |
michael@0 | 41 | 1. Specify the following Apache HTTP Server directives to suit your |
michael@0 | 42 | configuration. |
michael@0 | 43 | |
michael@0 | 44 | If mod_pywebsocket is not in the Python path, specify the following. |
michael@0 | 45 | <websock_lib> is the directory where mod_pywebsocket is installed. |
michael@0 | 46 | |
michael@0 | 47 | PythonPath "sys.path+['<websock_lib>']" |
michael@0 | 48 | |
michael@0 | 49 | Always specify the following. <websock_handlers> is the directory where |
michael@0 | 50 | user-written WebSocket handlers are placed. |
michael@0 | 51 | |
michael@0 | 52 | PythonOption mod_pywebsocket.handler_root <websock_handlers> |
michael@0 | 53 | PythonHeaderParserHandler mod_pywebsocket.headerparserhandler |
michael@0 | 54 | |
michael@0 | 55 | To limit the search for WebSocket handlers to a directory <scan_dir> |
michael@0 | 56 | under <websock_handlers>, configure as follows: |
michael@0 | 57 | |
michael@0 | 58 | PythonOption mod_pywebsocket.handler_scan <scan_dir> |
michael@0 | 59 | |
michael@0 | 60 | <scan_dir> is useful in saving scan time when <websock_handlers> |
michael@0 | 61 | contains many non-WebSocket handler files. |
michael@0 | 62 | |
michael@0 | 63 | If you want to support old handshake based on |
michael@0 | 64 | draft-hixie-thewebsocketprotocol-75: |
michael@0 | 65 | |
michael@0 | 66 | PythonOption mod_pywebsocket.allow_draft75 On |
michael@0 | 67 | |
michael@0 | 68 | If you want to allow handlers whose canonical path is not under the root |
michael@0 | 69 | directory (i.e. symbolic link is in root directory but its target is not), |
michael@0 | 70 | configure as follows: |
michael@0 | 71 | |
michael@0 | 72 | PythonOption mod_pywebsocket.allow_handlers_outside_root_dir On |
michael@0 | 73 | |
michael@0 | 74 | Example snippet of httpd.conf: |
michael@0 | 75 | (mod_pywebsocket is in /websock_lib, WebSocket handlers are in |
michael@0 | 76 | /websock_handlers, port is 80 for ws, 443 for wss.) |
michael@0 | 77 | |
michael@0 | 78 | <IfModule python_module> |
michael@0 | 79 | PythonPath "sys.path+['/websock_lib']" |
michael@0 | 80 | PythonOption mod_pywebsocket.handler_root /websock_handlers |
michael@0 | 81 | PythonHeaderParserHandler mod_pywebsocket.headerparserhandler |
michael@0 | 82 | </IfModule> |
michael@0 | 83 | |
michael@0 | 84 | 2. Tune Apache parameters for serving WebSocket. We'd like to note that at |
michael@0 | 85 | least TimeOut directive from core features and RequestReadTimeout |
michael@0 | 86 | directive from mod_reqtimeout should be modified not to kill connections |
michael@0 | 87 | in only a few seconds of idle time. |
michael@0 | 88 | |
michael@0 | 89 | 3. Verify installation. You can use example/console.html to poke the server. |
michael@0 | 90 | |
michael@0 | 91 | |
michael@0 | 92 | Writing WebSocket handlers: |
michael@0 | 93 | |
michael@0 | 94 | When a WebSocket request comes in, the resource name |
michael@0 | 95 | specified in the handshake is considered as if it is a file path under |
michael@0 | 96 | <websock_handlers> and the handler defined in |
michael@0 | 97 | <websock_handlers>/<resource_name>_wsh.py is invoked. |
michael@0 | 98 | |
michael@0 | 99 | For example, if the resource name is /example/chat, the handler defined in |
michael@0 | 100 | <websock_handlers>/example/chat_wsh.py is invoked. |
michael@0 | 101 | |
michael@0 | 102 | A WebSocket handler is composed of the following three functions: |
michael@0 | 103 | |
michael@0 | 104 | web_socket_do_extra_handshake(request) |
michael@0 | 105 | web_socket_transfer_data(request) |
michael@0 | 106 | web_socket_passive_closing_handshake(request) |
michael@0 | 107 | |
michael@0 | 108 | where: |
michael@0 | 109 | request: mod_python request. |
michael@0 | 110 | |
michael@0 | 111 | web_socket_do_extra_handshake is called during the handshake after the |
michael@0 | 112 | headers are successfully parsed and WebSocket properties (ws_location, |
michael@0 | 113 | ws_origin, and ws_resource) are added to request. A handler |
michael@0 | 114 | can reject the request by raising an exception. |
michael@0 | 115 | |
michael@0 | 116 | A request object has the following properties that you can use during the |
michael@0 | 117 | extra handshake (web_socket_do_extra_handshake): |
michael@0 | 118 | - ws_resource |
michael@0 | 119 | - ws_origin |
michael@0 | 120 | - ws_version |
michael@0 | 121 | - ws_location (Hixie 75 and HyBi 00 only) |
michael@0 | 122 | - ws_extensions (Hybi 06 and later) |
michael@0 | 123 | - ws_deflate (HyBi 06 and later) |
michael@0 | 124 | - ws_protocol |
michael@0 | 125 | - ws_requested_protocols (HyBi 06 and later) |
michael@0 | 126 | |
michael@0 | 127 | The last two are a bit tricky. |
michael@0 | 128 | |
michael@0 | 129 | For HyBi 06 and later, ws_protocol is always set to None when |
michael@0 | 130 | web_socket_do_extra_handshake is called. If ws_requested_protocols is not |
michael@0 | 131 | None, you must choose one subprotocol from this list and set it to |
michael@0 | 132 | ws_protocol. |
michael@0 | 133 | |
michael@0 | 134 | For Hixie 75 and HyBi 00, when web_socket_do_extra_handshake is called, |
michael@0 | 135 | ws_protocol is set to the value given by the client in |
michael@0 | 136 | Sec-WebSocket-Protocol (WebSocket-Protocol for Hixie 75) header or None if |
michael@0 | 137 | such header was not found in the opening handshake request. Finish extra |
michael@0 | 138 | handshake with ws_protocol untouched to accept the request subprotocol. |
michael@0 | 139 | Then, Sec-WebSocket-Protocol (or WebSocket-Protocol) header will be sent to |
michael@0 | 140 | the client in response with the same value as requested. Raise an exception |
michael@0 | 141 | in web_socket_do_extra_handshake to reject the requested subprotocol. |
michael@0 | 142 | |
michael@0 | 143 | web_socket_transfer_data is called after the handshake completed |
michael@0 | 144 | successfully. A handler can receive/send messages from/to the client |
michael@0 | 145 | using request. mod_pywebsocket.msgutil module provides utilities |
michael@0 | 146 | for data transfer. |
michael@0 | 147 | |
michael@0 | 148 | You can receive a message by the following statement. |
michael@0 | 149 | |
michael@0 | 150 | message = request.ws_stream.receive_message() |
michael@0 | 151 | |
michael@0 | 152 | This call blocks until any complete text frame arrives, and the payload data |
michael@0 | 153 | of the incoming frame will be stored into message. When you're using IETF |
michael@0 | 154 | HyBi 00 or later protocol, receive_message() will return None on receiving |
michael@0 | 155 | client-initiated closing handshake. When any error occurs, receive_message() |
michael@0 | 156 | will raise some exception. |
michael@0 | 157 | |
michael@0 | 158 | You can send a message by the following statement. |
michael@0 | 159 | |
michael@0 | 160 | request.ws_stream.send_message(message) |
michael@0 | 161 | |
michael@0 | 162 | Executing the following statement or just return-ing from |
michael@0 | 163 | web_socket_transfer_data cause connection close. |
michael@0 | 164 | |
michael@0 | 165 | request.ws_stream.close_connection() |
michael@0 | 166 | |
michael@0 | 167 | When you're using IETF HyBi 00 or later protocol, close_connection will wait |
michael@0 | 168 | for closing handshake acknowledgement coming from the client. When it |
michael@0 | 169 | couldn't receive a valid acknowledgement, raises an exception. |
michael@0 | 170 | |
michael@0 | 171 | web_socket_passive_closing_handshake is called after the server receives |
michael@0 | 172 | incoming closing frame from the client peer immediately. You can specify |
michael@0 | 173 | code and reason by return values. They are sent as a outgoing closing frame |
michael@0 | 174 | from the server. A request object has the following properties that you can |
michael@0 | 175 | use in web_socket_passive_closing_handshake. |
michael@0 | 176 | - ws_close_code |
michael@0 | 177 | - ws_close_reason |
michael@0 | 178 | |
michael@0 | 179 | A WebSocket handler must be thread-safe if the server (Apache or |
michael@0 | 180 | standalone.py) is configured to use threads. |
michael@0 | 181 | """ |
michael@0 | 182 | |
michael@0 | 183 | |
michael@0 | 184 | # vi:sts=4 sw=4 et tw=72 |