Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(299)

Side by Side Diff: Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py

Issue 314043002: [WebSocket] Add tests for permessage deflate split frames. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2012, Google Inc. 3 # Copyright 2012, Google Inc.
4 # All rights reserved. 4 # All rights reserved.
5 # 5 #
6 # Redistribution and use in source and binary forms, with or without 6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are 7 # modification, are permitted provided that the following conditions are
8 # met: 8 # met:
9 # 9 #
10 # * Redistributions of source code must retain the above copyright 10 # * Redistributions of source code must retain the above copyright
(...skipping 18 matching lines...) Expand all
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 31
32 32
33 """Standalone WebSocket server. 33 """Standalone WebSocket server.
34 34
35 Use this file to launch pywebsocket without Apache HTTP Server. 35 Use this file to launch pywebsocket without Apache HTTP Server.
36 36
37 37
38 BASIC USAGE 38 BASIC USAGE
39 ===========
39 40
40 Go to the src directory and run 41 Go to the src directory and run
41 42
42 $ python mod_pywebsocket/standalone.py [-p <ws_port>] 43 $ python mod_pywebsocket/standalone.py [-p <ws_port>]
43 [-w <websock_handlers>] 44 [-w <websock_handlers>]
44 [-d <document_root>] 45 [-d <document_root>]
45 46
46 <ws_port> is the port number to use for ws:// connection. 47 <ws_port> is the port number to use for ws:// connection.
47 48
48 <document_root> is the path to the root directory of HTML files. 49 <document_root> is the path to the root directory of HTML files.
49 50
50 <websock_handlers> is the path to the root directory of WebSocket handlers. 51 <websock_handlers> is the path to the root directory of WebSocket handlers.
51 If not specified, <document_root> will be used. See __init__.py (or 52 If not specified, <document_root> will be used. See __init__.py (or
52 run $ pydoc mod_pywebsocket) for how to write WebSocket handlers. 53 run $ pydoc mod_pywebsocket) for how to write WebSocket handlers.
53 54
54 For more detail and other options, run 55 For more detail and other options, run
55 56
56 $ python mod_pywebsocket/standalone.py --help 57 $ python mod_pywebsocket/standalone.py --help
57 58
58 or see _build_option_parser method below. 59 or see _build_option_parser method below.
59 60
60 For trouble shooting, adding "--log_level debug" might help you. 61 For trouble shooting, adding "--log_level debug" might help you.
61 62
62 63
63 TRY DEMO 64 TRY DEMO
65 ========
64 66
65 Go to the src directory and run 67 Go to the src directory and run standalone.py with -d option to set the
68 document root to the directory containing example HTMLs and handlers like this:
66 69
67 $ python standalone.py -d example 70 $ cd src
71 $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example
68 72
69 to launch pywebsocket with the sample handler and html on port 80. Open 73 to launch pywebsocket with the sample handler and html on port 80. Open
70 http://localhost/console.html, click the connect button, type something into 74 http://localhost/console.html, click the connect button, type something into
71 the text box next to the send button and click the send button. If everything 75 the text box next to the send button and click the send button. If everything
72 is working, you'll see the message you typed echoed by the server. 76 is working, you'll see the message you typed echoed by the server.
73 77
74 78
75 SUPPORTING TLS 79 USING TLS
80 =========
76 81
77 To support TLS, run standalone.py with -t, -k, and -c options. 82 To run the standalone server with TLS support, run it with -t, -k, and -c
83 options. When TLS is enabled, the standalone server accepts only TLS connection.
78 84
79 Note that when ssl module is used and the key/cert location is incorrect, 85 Note that when ssl module is used and the key/cert location is incorrect,
80 TLS connection silently fails while pyOpenSSL fails on startup. 86 TLS connection silently fails while pyOpenSSL fails on startup.
81 87
88 Example:
82 89
83 SUPPORTING CLIENT AUTHENTICATION 90 $ PYTHONPATH=. python mod_pywebsocket/standalone.py \
91 -d example \
92 -p 10443 \
93 -t \
94 -c ../test/cert/cert.pem \
95 -k ../test/cert/key.pem \
84 96
85 To support client authentication with TLS, run standalone.py with -t, -k, -c, 97 Note that when passing a relative path to -c and -k option, it will be resolved
86 and --tls-client-auth, and --tls-client-ca options. 98 using the document root directory as the base.
87 99
88 E.g., $./standalone.py -d ../example -p 10443 -t -c ../test/cert/cert.pem -k 100
89 ../test/cert/key.pem --tls-client-auth --tls-client-ca=../test/cert/cacert.pem 101 USING CLIENT AUTHENTICATION
102 ===========================
103
104 To run the standalone server with TLS client authentication support, run it with
105 --tls-client-auth and --tls-client-ca options in addition to ones required for
106 TLS support.
107
108 Example:
109
110 $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example -p 10443 -t \
111 -c ../test/cert/cert.pem -k ../test/cert/key.pem \
112 --tls-client-auth \
113 --tls-client-ca=../test/cert/cacert.pem
114
115 Note that when passing a relative path to --tls-client-ca option, it will be
116 resolved using the document root directory as the base.
90 117
91 118
92 CONFIGURATION FILE 119 CONFIGURATION FILE
120 ==================
93 121
94 You can also write a configuration file and use it by specifying the path to 122 You can also write a configuration file and use it by specifying the path to
95 the configuration file by --config option. Please write a configuration file 123 the configuration file by --config option. Please write a configuration file
96 following the documentation of the Python ConfigParser library. Name of each 124 following the documentation of the Python ConfigParser library. Name of each
97 entry must be the long version argument name. E.g. to set log level to debug, 125 entry must be the long version argument name. E.g. to set log level to debug,
98 add the following line: 126 add the following line:
99 127
100 log_level=debug 128 log_level=debug
101 129
102 For options which doesn't take value, please add some fake value. E.g. for 130 For options which doesn't take value, please add some fake value. E.g. for
103 --tls option, add the following line: 131 --tls option, add the following line:
104 132
105 tls=True 133 tls=True
106 134
107 Note that tls will be enabled even if you write tls=False as the value part is 135 Note that tls will be enabled even if you write tls=False as the value part is
108 fake. 136 fake.
109 137
110 When both a command line argument and a configuration file entry are set for 138 When both a command line argument and a configuration file entry are set for
111 the same configuration item, the command line value will override one in the 139 the same configuration item, the command line value will override one in the
112 configuration file. 140 configuration file.
113 141
114 142
115 THREADING 143 THREADING
144 =========
116 145
117 This server is derived from SocketServer.ThreadingMixIn. Hence a thread is 146 This server is derived from SocketServer.ThreadingMixIn. Hence a thread is
118 used for each request. 147 used for each request.
119 148
120 149
121 SECURITY WARNING 150 SECURITY WARNING
151 ================
122 152
123 This uses CGIHTTPServer and CGIHTTPServer is not secure. 153 This uses CGIHTTPServer and CGIHTTPServer is not secure.
124 It may execute arbitrary Python code or external programs. It should not be 154 It may execute arbitrary Python code or external programs. It should not be
125 used outside a firewall. 155 used outside a firewall.
126 """ 156 """
127 157
128 import BaseHTTPServer 158 import BaseHTTPServer
129 import CGIHTTPServer 159 import CGIHTTPServer
130 import SimpleHTTPServer 160 import SimpleHTTPServer
131 import SocketServer 161 import SocketServer
(...skipping 10 matching lines...) Expand all
142 import sys 172 import sys
143 import threading 173 import threading
144 import time 174 import time
145 175
146 from mod_pywebsocket import common 176 from mod_pywebsocket import common
147 from mod_pywebsocket import dispatch 177 from mod_pywebsocket import dispatch
148 from mod_pywebsocket import handshake 178 from mod_pywebsocket import handshake
149 from mod_pywebsocket import http_header_util 179 from mod_pywebsocket import http_header_util
150 from mod_pywebsocket import memorizingfile 180 from mod_pywebsocket import memorizingfile
151 from mod_pywebsocket import util 181 from mod_pywebsocket import util
182 from mod_pywebsocket.xhr_benchmark_handler import XHRBenchmarkHandler
152 183
153 184
154 _DEFAULT_LOG_MAX_BYTES = 1024 * 256 185 _DEFAULT_LOG_MAX_BYTES = 1024 * 256
155 _DEFAULT_LOG_BACKUP_COUNT = 5 186 _DEFAULT_LOG_BACKUP_COUNT = 5
156 187
157 _DEFAULT_REQUEST_QUEUE_SIZE = 128 188 _DEFAULT_REQUEST_QUEUE_SIZE = 128
158 189
159 # 1024 is practically large enough to contain WebSocket handshake lines. 190 # 1024 is practically large enough to contain WebSocket handshake lines.
160 _MAX_MEMORIZED_LINES = 1024 191 _MAX_MEMORIZED_LINES = 1024
161 192
(...skipping 493 matching lines...) Expand 10 before | Expand all | Expand 10 after
655 # Overrides CGIHTTPServerRequestHandler.cgi_directories. 686 # Overrides CGIHTTPServerRequestHandler.cgi_directories.
656 self.cgi_directories = self._options.cgi_directories 687 self.cgi_directories = self._options.cgi_directories
657 # Replace CGIHTTPRequestHandler.is_executable method. 688 # Replace CGIHTTPRequestHandler.is_executable method.
658 if self._options.is_executable_method is not None: 689 if self._options.is_executable_method is not None:
659 self.is_executable = self._options.is_executable_method 690 self.is_executable = self._options.is_executable_method
660 691
661 # This actually calls BaseRequestHandler.__init__. 692 # This actually calls BaseRequestHandler.__init__.
662 CGIHTTPServer.CGIHTTPRequestHandler.__init__( 693 CGIHTTPServer.CGIHTTPRequestHandler.__init__(
663 self, request, client_address, server) 694 self, request, client_address, server)
664 695
665 def _xhr_send_benchmark_helper(self):
666 content_length = int(self.headers.getheader('Content-Length'))
667
668 self._logger.debug('Requested to receive %s bytes', content_length)
669
670 RECEIVE_BLOCK_SIZE = 1024 * 1024
671
672 bytes_to_receive = content_length
673 while bytes_to_receive > 0:
674 bytes_to_receive_in_this_loop = bytes_to_receive
675 if bytes_to_receive_in_this_loop > RECEIVE_BLOCK_SIZE:
676 bytes_to_receive_in_this_loop = RECEIVE_BLOCK_SIZE
677 received_data = self.rfile.read(bytes_to_receive_in_this_loop)
678 for c in received_data:
679 if c != 'a':
680 self._logger.debug('Request body verification failed')
681 return
682 bytes_to_receive -= len(received_data)
683 if bytes_to_receive < 0:
684 self._logger.debug('Received %d more bytes than expected' %
685 (-bytes_to_receive))
686 return
687
688 # Return the number of received bytes back to the client.
689 response_body = '%d' % content_length
690 self.wfile.write(
691 'HTTP/1.1 200 OK\r\n'
692 'Content-Type: text/html\r\n'
693 'Content-Length: %d\r\n'
694 '\r\n%s' % (len(response_body), response_body))
695 self.wfile.flush()
696
697 def _xhr_receive_benchmark_helper(self):
698 content_length = self.headers.getheader('Content-Length')
699 request_body = self.rfile.read(int(content_length))
700
701 request_array = request_body.split(' ')
702 if len(request_array) < 2:
703 self._logger.debug('Malformed request body: %r', request_body)
704 return
705
706 # Parse the size parameter.
707 bytes_to_send = request_array[0]
708 try:
709 bytes_to_send = int(bytes_to_send)
710 except ValueError, e:
711 self._logger.debug('Malformed size parameter: %r', bytes_to_send)
712 return
713 self._logger.debug('Requested to send %s bytes', bytes_to_send)
714
715 # Parse the transfer encoding parameter.
716 chunked_mode = False
717 mode_parameter = request_array[1]
718 if mode_parameter == 'chunked':
719 self._logger.debug('Requested chunked transfer encoding')
720 chunked_mode = True
721 elif mode_parameter != 'none':
722 self._logger.debug('Invalid mode parameter: %r', mode_parameter)
723 return
724
725 # Write a header
726 response_header = (
727 'HTTP/1.1 200 OK\r\n'
728 'Content-Type: application/octet-stream\r\n')
729 if chunked_mode:
730 response_header += 'Transfer-Encoding: chunked\r\n\r\n'
731 else:
732 response_header += (
733 'Content-Length: %d\r\n\r\n' % bytes_to_send)
734 self.wfile.write(response_header)
735 self.wfile.flush()
736
737 # Write a body
738 SEND_BLOCK_SIZE = 1024 * 1024
739
740 while bytes_to_send > 0:
741 bytes_to_send_in_this_loop = bytes_to_send
742 if bytes_to_send_in_this_loop > SEND_BLOCK_SIZE:
743 bytes_to_send_in_this_loop = SEND_BLOCK_SIZE
744
745 if chunked_mode:
746 self.wfile.write('%x\r\n' % bytes_to_send_in_this_loop)
747 self.wfile.write('a' * bytes_to_send_in_this_loop)
748 if chunked_mode:
749 self.wfile.write('\r\n')
750 self.wfile.flush()
751
752 bytes_to_send -= bytes_to_send_in_this_loop
753
754 if chunked_mode:
755 self.wfile.write('0\r\n\r\n')
756 self.wfile.flush()
757
758 def parse_request(self): 696 def parse_request(self):
759 """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request. 697 """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
760 698
761 Return True to continue processing for HTTP(S), False otherwise. 699 Return True to continue processing for HTTP(S), False otherwise.
762 700
763 See BaseHTTPRequestHandler.handle_one_request method which calls 701 See BaseHTTPRequestHandler.handle_one_request method which calls
764 this method to understand how the return value will be handled. 702 this method to understand how the return value will be handled.
765 """ 703 """
766 704
767 # We hook parse_request method, but also call the original 705 # We hook parse_request method, but also call the original
(...skipping 16 matching lines...) Expand all
784 'Basic realm="Pywebsocket"') 722 'Basic realm="Pywebsocket"')
785 self.end_headers() 723 self.end_headers()
786 self._logger.info('Request basic authentication') 724 self._logger.info('Request basic authentication')
787 return True 725 return True
788 726
789 host, port, resource = http_header_util.parse_uri(self.path) 727 host, port, resource = http_header_util.parse_uri(self.path)
790 728
791 # Special paths for XMLHttpRequest benchmark 729 # Special paths for XMLHttpRequest benchmark
792 xhr_benchmark_helper_prefix = '/073be001e10950692ccbf3a2ad21c245' 730 xhr_benchmark_helper_prefix = '/073be001e10950692ccbf3a2ad21c245'
793 if resource == (xhr_benchmark_helper_prefix + '_send'): 731 if resource == (xhr_benchmark_helper_prefix + '_send'):
794 self._xhr_send_benchmark_helper() 732 xhr_benchmark_handler = XHRBenchmarkHandler(
733 self.headers, self.rfile, self.wfile)
734 xhr_benchmark_handler.do_send()
795 return False 735 return False
796 if resource == (xhr_benchmark_helper_prefix + '_receive'): 736 if resource == (xhr_benchmark_helper_prefix + '_receive'):
797 self._xhr_receive_benchmark_helper() 737 xhr_benchmark_handler = XHRBenchmarkHandler(
738 self.headers, self.rfile, self.wfile)
739 xhr_benchmark_handler.do_receive()
798 return False 740 return False
799 741
800 if resource is None: 742 if resource is None:
801 self._logger.info('Invalid URI: %r', self.path) 743 self._logger.info('Invalid URI: %r', self.path)
802 self._logger.info('Fallback to CGIHTTPRequestHandler') 744 self._logger.info('Fallback to CGIHTTPRequestHandler')
803 return True 745 return True
804 server_options = self.server.websocket_server_options 746 server_options = self.server.websocket_server_options
805 if host is not None: 747 if host is not None:
806 validation_host = server_options.validation_host 748 validation_host = server_options.validation_host
807 if validation_host is not None and host != validation_host: 749 if validation_host is not None and host != validation_host:
(...skipping 426 matching lines...) Expand 10 before | Expand all | Expand 10 after
1234 logging.critical('mod_pywebsocket: %s' % e) 1176 logging.critical('mod_pywebsocket: %s' % e)
1235 logging.critical('mod_pywebsocket: %s' % util.get_stack_trace()) 1177 logging.critical('mod_pywebsocket: %s' % util.get_stack_trace())
1236 sys.exit(1) 1178 sys.exit(1)
1237 1179
1238 1180
1239 if __name__ == '__main__': 1181 if __name__ == '__main__':
1240 _main(sys.argv[1:]) 1182 _main(sys.argv[1:])
1241 1183
1242 1184
1243 # vi:sts=4 sw=4 et 1185 # vi:sts=4 sw=4 et
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698