diff options
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py')
-rw-r--r-- | WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py new file mode 100644 index 0000000..2b976e1 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +"""Web Socket Echo client. + +This is an example Web Socket client that talks with echo_wsh.py. +This may be useful for checking mod_pywebsocket installation. + +Note: +This code is far from robust, e.g., we cut corners in handshake. +""" + + +import codecs +from optparse import OptionParser +import socket +import sys + + +_TIMEOUT_SEC = 10 + +_DEFAULT_PORT = 80 +_DEFAULT_SECURE_PORT = 443 +_UNDEFINED_PORT = -1 + +_UPGRADE_HEADER = 'Upgrade: WebSocket\r\n' +_CONNECTION_HEADER = 'Connection: Upgrade\r\n' +_EXPECTED_RESPONSE = ( + 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + + _UPGRADE_HEADER + + _CONNECTION_HEADER) + +_GOODBYE_MESSAGE = 'Goodbye' + + +def _method_line(resource): + return 'GET %s HTTP/1.1\r\n' % resource + + +def _origin_header(origin): + return 'Origin: %s\r\n' % origin + + +class _TLSSocket(object): + """Wrapper for a TLS connection.""" + + def __init__(self, raw_socket): + self._ssl = socket.ssl(raw_socket) + + def send(self, bytes): + return self._ssl.write(bytes) + + def recv(self, size=-1): + return self._ssl.read(size) + + def close(self): + # Nothing to do. + pass + + +class EchoClient(object): + """Web Socket echo client.""" + + def __init__(self, options): + self._options = options + self._socket = None + + def run(self): + """Run the client. + + Shake hands and then repeat sending message and receiving its echo. + """ + self._socket = socket.socket() + self._socket.settimeout(self._options.socket_timeout) + try: + self._socket.connect((self._options.server_host, + self._options.server_port)) + if self._options.use_tls: + self._socket = _TLSSocket(self._socket) + self._handshake() + for line in self._options.message.split(',') + [_GOODBYE_MESSAGE]: + frame = '\x00' + line.encode('utf-8') + '\xff' + self._socket.send(frame) + if self._options.verbose: + print 'Send: %s' % line + received = self._socket.recv(len(frame)) + if received != frame: + raise Exception('Incorrect echo: %r' % received) + if self._options.verbose: + print 'Recv: %s' % received[1:-1].decode('utf-8', + 'replace') + finally: + self._socket.close() + + def _handshake(self): + self._socket.send(_method_line(self._options.resource)) + self._socket.send(_UPGRADE_HEADER) + self._socket.send(_CONNECTION_HEADER) + self._socket.send(self._format_host_header()) + self._socket.send(_origin_header(self._options.origin)) + self._socket.send('\r\n') + + for expected_char in _EXPECTED_RESPONSE: + received = self._socket.recv(1)[0] + if expected_char != received: + raise Exception('Handshake failure') + # We cut corners and skip other headers. + self._skip_headers() + + def _skip_headers(self): + terminator = '\r\n\r\n' + pos = 0 + while pos < len(terminator): + received = self._socket.recv(1)[0] + if received == terminator[pos]: + pos += 1 + elif received == terminator[0]: + pos = 1 + else: + pos = 0 + + def _format_host_header(self): + host = 'Host: ' + self._options.server_host + if ((not self._options.use_tls and + self._options.server_port != _DEFAULT_PORT) or + (self._options.use_tls and + self._options.server_port != _DEFAULT_SECURE_PORT)): + host += ':' + str(self._options.server_port) + host += '\r\n' + return host + + +def main(): + sys.stdout = codecs.getwriter('utf-8')(sys.stdout) + + parser = OptionParser() + parser.add_option('-s', '--server-host', '--server_host', + dest='server_host', type='string', + default='localhost', help='server host') + parser.add_option('-p', '--server-port', '--server_port', + dest='server_port', type='int', + default=_UNDEFINED_PORT, help='server port') + parser.add_option('-o', '--origin', dest='origin', type='string', + default='http://localhost/', help='origin') + parser.add_option('-r', '--resource', dest='resource', type='string', + default='/echo', help='resource path') + parser.add_option('-m', '--message', dest='message', type='string', + help=('comma-separated messages to send excluding "%s" ' + 'that is always sent at the end' % + _GOODBYE_MESSAGE)) + parser.add_option('-q', '--quiet', dest='verbose', action='store_false', + default=True, help='suppress messages') + parser.add_option('-t', '--tls', dest='use_tls', action='store_true', + default=False, help='use TLS (wss://)') + parser.add_option('-k', '--socket-timeout', '--socket_timeout', + dest='socket_timeout', type='int', default=_TIMEOUT_SEC, + help='Timeout(sec) for sockets') + + (options, unused_args) = parser.parse_args() + + # Default port number depends on whether TLS is used. + if options.server_port == _UNDEFINED_PORT: + if options.use_tls: + options.server_port = _DEFAULT_SECURE_PORT + else: + options.server_port = _DEFAULT_PORT + + # optparse doesn't seem to handle non-ascii default values. + # Set default message here. + if not options.message: + options.message = u'Hello,\u65e5\u672c' # "Japan" in Japanese + + EchoClient(options).run() + + +if __name__ == '__main__': + main() + + +# vi:sts=4 sw=4 et |