Total Time : 12.5950 CPU second
Total Func Call: 1285 function calls
| Top 20 functions data | |||||
|---|---|---|---|---|---|
| ncalls | tottime | percall | cumtime | percall | filename:lineno(function) |
| 9 | 7.4059 | 0.8229 | 7.4060 | 0.8229 | /usr/lib/python2.6/socket.py : 194 (accept) |
| 13 | 5.0035 | 0.3849 | 5.0041 | 0.3849 | /usr/lib/python2.6/threading.py : 228 (wait) |
| 1 | 0.1006 | 0.1006 | 0.1155 | 0.1155 | /var/lib/python-support/python2.6/web/wsgiserver/__init__.py : 1283 (start) |
| 1 | 0.0254 | 0.0254 | 0.0316 | 0.0316 | /usr/lib/python2.6/mimetypes.py : 199 (readfp) |
| 23 | 0.0090 | 0.0004 | 0.0092 | 0.0004 | /usr/lib/python2.6/threading.py : 179 (__init__) |
| 610 | 0.0079 | 0.0000 | 0.0079 | 0.0000 | /usr/lib/python2.6/mimetypes.py : 71 (add_type) |
| 1 | 0.0061 | 0.0061 | 0.0068 | 0.0068 | /var/lib/python-support/python2.6/OpenSSL/tsafe.py : 9 (Connection) |
| 1 | 0.0058 | 0.0058 | 0.0060 | 0.0060 | /var/lib/python-support/python2.6/web/wsgiserver/__init__.py : 1366 (SSLConnection) |
| 1 | 0.0037 | 0.0037 | 0.0238 | 0.0238 | /var/lib/python-support/python2.6/web/wsgiserver/__init__.py : 77 (<module>) |
| 1 | 0.0032 | 0.0032 | 0.0102 | 0.0102 | /var/lib/python-support/python2.6/OpenSSL/__init__.py : 10 (<module>) |
| 1 | 0.0025 | 0.0025 | 12.5948 | 12.5948 | /var/lib/python-support/python2.6/web/httpserver.py : 130 (runsimple) |
| 1 | 0.0025 | 0.0025 | 0.0404 | 0.0404 | /usr/lib/python2.6/SimpleHTTPServer.py : 6 (<module>) |
| 77 | 0.0013 | 0.0000 | 0.0013 | 0.0000 | <string> : 1 (<module>) |
| 10 | 0.0011 | 0.0001 | 0.0039 | 0.0004 | /usr/lib/python2.6/threading.py : 461 (start) |
| 1 | 0.0010 | 0.0010 | 0.0017 | 0.0017 | /usr/lib/python2.6/BaseHTTPServer.py : 18 (<module>) |
| 1 | 0.0010 | 0.0010 | 0.0027 | 0.0027 | /usr/lib/python2.6/mimetypes.py : 57 (__init__) |
| 1 | 0.0009 | 0.0009 | 0.0018 | 0.0018 | /usr/lib/python2.6/Queue.py : 1 (<module>) |
| 1 | 0.0008 | 0.0008 | 0.0008 | 0.0008 | /usr/lib/python2.6/heapq.py : 31 (<module>) |
| 1 | 0.0008 | 0.0008 | 0.0008 | 0.0008 | /usr/lib/python2.6/shutil.py : 5 (<module>) |
| 10 | 0.0006 | 0.0001 | 0.0106 | 0.0011 | /usr/lib/python2.6/threading.py : 424 (__init__) |
| /usr/lib/python2.6/BaseHTTPServer.py | ||
|---|---|---|
| time | num | code |
| 1 | """HTTP server base class. |
|
| 2 | ||
| 3 | Note: the class in this module doesn't implement any HTTP request; see |
|
| 4 | SimpleHTTPServer for simple implementations of GET, HEAD and POST |
|
| 5 | (including CGI scripts). It does, however, optionally implement HTTP/1.1 |
|
| 6 | persistent connections, as of version 0.3. |
|
| 7 | ||
| 8 | Contents: |
|
| 9 | ||
| 10 | - BaseHTTPRequestHandler: HTTP request handler base class |
|
| 11 | - test: test function |
|
| 12 | ||
| 13 | XXX To do: |
|
| 14 | ||
| 15 | - log requests even later (to capture byte count) |
|
| 16 | - log user-agent header and other interesting goodies |
|
| 17 | - send error log to separate file |
|
| 0.000895 sec | 18 | """ |
| 19 | ||
| 20 | ||
| 21 | # See also: |
|
| 22 | # |
|
| 23 | # HTTP Working Group T. Berners-Lee |
|
| 24 | # INTERNET-DRAFT R. T. Fielding |
|
| 25 | # <draft-ietf-http-v10-spec-00.txt> H. Frystyk Nielsen |
|
| 26 | # Expires September 8, 1995 March 8, 1995 |
|
| 27 | # |
|
| 28 | # URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt |
|
| 29 | # |
|
| 30 | # and |
|
| 31 | # |
|
| 32 | # Network Working Group R. Fielding |
|
| 33 | # Request for Comments: 2616 et al |
|
| 34 | # Obsoletes: 2068 June 1999 |
|
| 35 | # Category: Standards Track |
|
| 36 | # |
|
| 37 | # URL: http://www.faqs.org/rfcs/rfc2616.html |
|
| 38 | ||
| 39 | # Log files |
|
| 40 | # --------- |
|
| 41 | # |
|
| 42 | # Here's a quote from the NCSA httpd docs about log file format. |
|
| 43 | # |
|
| 44 | # | The logfile format is as follows. Each line consists of: |
|
| 45 | # | |
|
| 46 | # | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb |
|
| 47 | # | |
|
| 48 | # | host: Either the DNS name or the IP number of the remote client |
|
| 49 | # | rfc931: Any information returned by identd for this person, |
|
| 50 | # | - otherwise. |
|
| 51 | # | authuser: If user sent a userid for authentication, the user name, |
|
| 52 | # | - otherwise. |
|
| 53 | # | DD: Day |
|
| 54 | # | Mon: Month (calendar name) |
|
| 55 | # | YYYY: Year |
|
| 56 | # | hh: hour (24-hour format, the machine's timezone) |
|
| 57 | # | mm: minutes |
|
| 58 | # | ss: seconds |
|
| 59 | # | request: The first line of the HTTP request as sent by the client. |
|
| 60 | # | ddd: the status code returned by the server, - if not available. |
|
| 61 | # | bbbb: the total number of bytes sent, |
|
| 62 | # | *not including the HTTP/1.0 header*, - if not available |
|
| 63 | # | |
|
| 64 | # | You can determine the name of the file accessed through request. |
|
| 65 | # |
|
| 66 | # (Actually, the latter is only true if you know the server configuration |
|
| 67 | # at the time the request was made!) |
|
| 68 | ||
| 2e-06 sec | 69 | __version__ = "0.3" |
| 70 | ||
| 3e-06 sec | 71 | __all__ = ["HTTPServer", "BaseHTTPRequestHandler"] |
| 72 | ||
| 3e-06 sec | 73 | import sys |
| 9e-06 sec | 74 | import time |
| 5e-06 sec | 75 | import socket # For gethostbyaddr() |
| 5e-06 sec | 76 | from warnings import filterwarnings, catch_warnings |
| 1.3e-05 sec | 77 | with catch_warnings(): |
| 3e-06 sec | 78 | if sys.py3kwarning: |
| 79 | filterwarnings("ignore", ".*mimetools has been removed",
|
|
| 80 | DeprecationWarning) |
|
| 3e-06 sec | 81 | import mimetools |
| 6e-06 sec | 82 | import SocketServer |
| 83 | ||
| 84 | # Default error message template |
|
| 85 | DEFAULT_ERROR_MESSAGE = """\ |
|
| 86 | <head> |
|
| 87 | <title>Error response</title> |
|
| 88 | </head> |
|
| 89 | <body> |
|
| 90 | <h1>Error response</h1> |
|
| 91 | <p>Error code %(code)d. |
|
| 92 | <p>Message: %(message)s. |
|
| 93 | <p>Error code explanation: %(code)s = %(explain)s. |
|
| 94 | </body> |
|
| 2.1e-05 sec | 95 | """ |
| 96 | ||
| 3e-06 sec | 97 | DEFAULT_ERROR_CONTENT_TYPE = "text/html" |
| 98 | ||
| 2e-06 sec | 99 | def _quote_html(html): |
| 100 | return html.replace("&", "&").replace("<", "<").replace(">", ">")
|
|
| 101 | ||
| 1e-05 sec | 102 | class HTTPServer(SocketServer.TCPServer): |
| 103 | ||
| 3e-06 sec | 104 | allow_reuse_address = 1 # Seems to make sense in testing environment |
| 105 | ||
| 1e-06 sec | 106 | def server_bind(self): |
| 107 | """Override server_bind to store the server name.""" |
|
| 108 | SocketServer.TCPServer.server_bind(self) |
|
| 109 | host, port = self.socket.getsockname()[:2] |
|
| 110 | self.server_name = socket.getfqdn(host) |
|
| 111 | self.server_port = port |
|
| 112 | ||
| 113 | ||
| 1.1e-05 sec | 114 | class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler): |
| 115 | ||
| 116 | """HTTP request handler base class. |
|
| 117 | ||
| 118 | The following explanation of HTTP serves to guide you through the |
|
| 119 | code as well as to expose any misunderstandings I may have about |
|
| 120 | HTTP (so you don't need to read the code to figure out I'm wrong |
|
| 121 | :-). |
|
| 122 | ||
| 123 | HTTP (HyperText Transfer Protocol) is an extensible protocol on |
|
| 124 | top of a reliable stream transport (e.g. TCP/IP). The protocol |
|
| 125 | recognizes three parts to a request: |
|
| 126 | ||
| 127 | 1. One line identifying the request type and path |
|
| 128 | 2. An optional set of RFC-822-style headers |
|
| 129 | 3. An optional data part |
|
| 130 | ||
| 131 | The headers and data are separated by a blank line. |
|
| 132 | ||
| 133 | The first line of the request has the form |
|
| 134 | ||
| 135 | <command> <path> <version> |
|
| 136 | ||
| 137 | where <command> is a (case-sensitive) keyword such as GET or POST, |
|
| 138 | <path> is a string containing path information for the request, |
|
| 139 | and <version> should be the string "HTTP/1.0" or "HTTP/1.1". |
|
| 140 | <path> is encoded using the URL encoding scheme (using %xx to signify |
|
| 141 | the ASCII character with hex code xx). |
|
| 142 | ||
| 143 | The specification specifies that lines are separated by CRLF but |
|
| 144 | for compatibility with the widest range of clients recommends |
|
| 145 | servers also handle LF. Similarly, whitespace in the request line |
|
| 146 | is treated sensibly (allowing multiple spaces between components |
|
| 147 | and allowing trailing whitespace). |
|
| 148 | ||
| 149 | Similarly, for output, lines ought to be separated by CRLF pairs |
|
| 150 | but most clients grok LF characters just fine. |
|
| 151 | ||
| 152 | If the first line of the request has the form |
|
| 153 | ||
| 154 | <command> <path> |
|
| 155 | ||
| 156 | (i.e. <version> is left out) then this is assumed to be an HTTP |
|
| 157 | 0.9 request; this form has no optional headers and data part and |
|
| 158 | the reply consists of just the data. |
|
| 159 | ||
| 160 | The reply form of the HTTP 1.x protocol again has three parts: |
|
| 161 | ||
| 162 | 1. One line giving the response code |
|
| 163 | 2. An optional set of RFC-822-style headers |
|
| 164 | 3. The data |
|
| 165 | ||
| 166 | Again, the headers and data are separated by a blank line. |
|
| 167 | ||
| 168 | The response code line has the form |
|
| 169 | ||
| 170 | <version> <responsecode> <responsestring> |
|
| 171 | ||
| 172 | where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
|
|
| 173 | <responsecode> is a 3-digit response code indicating success or |
|
| 174 | failure of the request, and <responsestring> is an optional |
|
| 175 | human-readable string explaining what the response code means. |
|
| 176 | ||
| 177 | This server parses the request and the headers, and then calls a |
|
| 178 | function specific to the request type (<command>). Specifically, |
|
| 179 | a request SPAM will be handled by a method do_SPAM(). If no |
|
| 180 | such method exists the server sends an error response to the |
|
| 181 | client. If it exists, it is called with no arguments: |
|
| 182 | ||
| 183 | do_SPAM() |
|
| 184 | ||
| 185 | Note that the request name is case sensitive (i.e. SPAM and spam |
|
| 186 | are different requests). |
|
| 187 | ||
| 188 | The various request details are stored in instance variables: |
|
| 189 | ||
| 190 | - client_address is the client IP address in the form (host, |
|
| 191 | port); |
|
| 192 | ||
| 193 | - command, path and version are the broken-down request line; |
|
| 194 | ||
| 195 | - headers is an instance of mimetools.Message (or a derived |
|
| 196 | class) containing the header information; |
|
| 197 | ||
| 198 | - rfile is a file object open for reading positioned at the |
|
| 199 | start of the optional input data part; |
|
| 200 | ||
| 201 | - wfile is a file object open for writing. |
|
| 202 | ||
| 203 | IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING! |
|
| 204 | ||
| 205 | The first thing to be written must be the response line. Then |
|
| 206 | follow 0 or more header lines, then a blank line, and then the |
|
| 207 | actual data (if any). The meaning of the header lines depends on |
|
| 208 | the command executed by the server; in most cases, when data is |
|
| 209 | returned, there should be at least one header line of the form |
|
| 210 | ||
| 211 | Content-type: <type>/<subtype> |
|
| 212 | ||
| 213 | where <type> and <subtype> should be registered MIME types, |
|
| 214 | e.g. "text/html" or "text/plain". |
|
| 215 | ||
| 2e-06 sec | 216 | """ |
| 217 | ||
| 218 | # The Python system version, truncated to its first component. |
|
| 2e-06 sec | 219 | sys_version = "Python/" + sys.version.split()[0] |
| 220 | ||
| 221 | # The server software version. You may want to override this. |
|
| 222 | # The format is multiple whitespace-separated strings, |
|
| 223 | # where each string is of the form name[/version]. |
|
| 1.5e-05 sec | 224 | server_version = "BaseHTTP/" + __version__ |
| 225 | ||
| 226 | # The default request version. This only affects responses up until |
|
| 227 | # the point where the request line is parsed, so it mainly decides what |
|
| 228 | # the client gets back when sending a malformed request line. |
|
| 229 | # Most web servers default to HTTP 0.9, i.e. don't send a status line. |
|
| 2e-06 sec | 230 | default_request_version = "HTTP/0.9" |
| 231 | ||
| 3e-06 sec | 232 | def parse_request(self): |
| 233 | """Parse a request (internal). |
|
| 234 | ||
| 235 | The request should be stored in self.raw_requestline; the results |
|
| 236 | are in self.command, self.path, self.request_version and |
|
| 237 | self.headers. |
|
| 238 | ||
| 239 | Return True for success, False for failure; on failure, an |
|
| 240 | error is sent back. |
|
| 241 | ||
| 242 | """ |
|
| 243 | self.command = None # set in case of error on the first line |
|
| 244 | self.request_version = version = self.default_request_version |
|
| 245 | self.close_connection = 1 |
|
| 246 | requestline = self.raw_requestline |
|
| 247 | if requestline[-2:] == '\r\n': |
|
| 248 | requestline = requestline[:-2] |
|
| 249 | elif requestline[-1:] == '\n': |
|
| 250 | requestline = requestline[:-1] |
|
| 251 | self.requestline = requestline |
|
| 252 | words = requestline.split() |
|
| 253 | if len(words) == 3: |
|
| 254 | [command, path, version] = words |
|
| 255 | if version[:5] != 'HTTP/': |
|
| 256 | self.send_error(400, "Bad request version (%r)" % version) |
|
| 257 | return False |
|
| 258 | try: |
|
| 259 | base_version_number = version.split('/', 1)[1]
|
|
| 260 | version_number = base_version_number.split(".")
|
|
| 261 | # RFC 2145 section 3.1 says there can be only one "." and |
|
| 262 | # - major and minor numbers MUST be treated as |
|
| 263 | # separate integers; |
|
| 264 | # - HTTP/2.4 is a lower version than HTTP/2.13, which in |
|
| 265 | # turn is lower than HTTP/12.3; |
|
| 266 | # - Leading zeros MUST be ignored by recipients. |
|
| 267 | if len(version_number) != 2: |
|
| 268 | raise ValueError |
|
| 269 | version_number = int(version_number[0]), int(version_number[1]) |
|
| 270 | except (ValueError, IndexError): |
|
| 271 | self.send_error(400, "Bad request version (%r)" % version) |
|
| 272 | return False |
|
| 273 | if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": |
|
| 274 | self.close_connection = 0 |
|
| 275 | if version_number >= (2, 0): |
|
| 276 | self.send_error(505, |
|
| 277 | "Invalid HTTP Version (%s)" % base_version_number) |
|
| 278 | return False |
|
| 279 | elif len(words) == 2: |
|
| 280 | [command, path] = words |
|
| 281 | self.close_connection = 1 |
|
| 282 | if command != 'GET': |
|
| 283 | self.send_error(400, |
|
| 284 | "Bad HTTP/0.9 request type (%r)" % command) |
|
| 285 | return False |
|
| 286 | elif not words: |
|
| 287 | return False |
|
| 288 | else: |
|
| 289 | self.send_error(400, "Bad request syntax (%r)" % requestline) |
|
| 290 | return False |
|
| 291 | self.command, self.path, self.request_version = command, path, version |
|
| 292 | ||
| 293 | # Examine the headers and look for a Connection directive |
|
| 294 | self.headers = self.MessageClass(self.rfile, 0) |
|
| 295 | ||
| 296 | conntype = self.headers.get('Connection', "")
|
|
| 297 | if conntype.lower() == 'close': |
|
| 298 | self.close_connection = 1 |
|
| 299 | elif (conntype.lower() == 'keep-alive' and |
|
| 300 | self.protocol_version >= "HTTP/1.1"): |
|
| 301 | self.close_connection = 0 |
|
| 302 | return True |
|
| 303 | ||
| 3e-06 sec | 304 | def handle_one_request(self): |
| 305 | """Handle a single HTTP request. |
|
| 306 | ||
| 307 | You normally don't need to override this method; see the class |
|
| 308 | __doc__ string for information on how to handle specific HTTP |
|
| 309 | commands such as GET and POST. |
|
| 310 | ||
| 311 | """ |
|
| 312 | self.raw_requestline = self.rfile.readline() |
|
| 313 | if not self.raw_requestline: |
|
| 314 | self.close_connection = 1 |
|
| 315 | return |
|
| 316 | if not self.parse_request(): # An error code has been sent, just exit |
|
| 317 | return |
|
| 318 | mname = 'do_' + self.command |
|
| 319 | if not hasattr(self, mname): |
|
| 320 | self.send_error(501, "Unsupported method (%r)" % self.command) |
|
| 321 | return |
|
| 322 | method = getattr(self, mname) |
|
| 323 | method() |
|
| 324 | ||
| 3e-06 sec | 325 | def handle(self): |
| 326 | """Handle multiple requests if necessary.""" |
|
| 327 | self.close_connection = 1 |
|
| 328 | ||
| 329 | self.handle_one_request() |
|
| 330 | while not self.close_connection: |
|
| 331 | self.handle_one_request() |
|
| 332 | ||
| 2e-06 sec | 333 | def send_error(self, code, message=None): |
| 334 | """Send and log an error reply. |
|
| 335 | ||
| 336 | Arguments are the error code, and a detailed message. |
|
| 337 | The detailed message defaults to the short entry matching the |
|
| 338 | response code. |
|
| 339 | ||
| 340 | This sends an error response (so it must be called before any |
|
| 341 | output has been generated), logs the error, and finally sends |
|
| 342 | a piece of HTML explaining the error to the user. |
|
| 343 | ||
| 344 | """ |
|
| 345 | ||
| 346 | try: |
|
| 347 | short, long = self.responses[code] |
|
| 348 | except KeyError: |
|
| 349 | short, long = '???', '???' |
|
| 350 | if message is None: |
|
| 351 | message = short |
|
| 352 | explain = long |
|
| 353 | self.log_error("code %d, message %s", code, message)
|
|
| 354 | # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201) |
|
| 355 | content = (self.error_message_format % |
|
| 356 | {'code': code, 'message': _quote_html(message), 'explain': explain})
|
|
| 357 | self.send_response(code, message) |
|
| 358 | self.send_header("Content-Type", self.error_content_type)
|
|
| 359 | self.send_header('Connection', 'close')
|
|
| 360 | self.end_headers() |
|
| 361 | if self.command != 'HEAD' and code >= 200 and code not in (204, 304): |
|
| 362 | self.wfile.write(content) |
|
| 363 | ||
| 3e-06 sec | 364 | error_message_format = DEFAULT_ERROR_MESSAGE |
| 2e-06 sec | 365 | error_content_type = DEFAULT_ERROR_CONTENT_TYPE |
| 366 | ||
| 3e-06 sec | 367 | def send_response(self, code, message=None): |
| 368 | """Send the response header and log the response code. |
|
| 369 | ||
| 370 | Also send two standard headers with the server software |
|
| 371 | version and the current date. |
|
| 372 | ||
| 373 | """ |
|
| 374 | self.log_request(code) |
|
| 375 | if message is None: |
|
| 376 | if code in self.responses: |
|
| 377 | message = self.responses[code][0] |
|
| 378 | else: |
|
| 379 | message = '' |
|
| 380 | if self.request_version != 'HTTP/0.9': |
|
| 381 | self.wfile.write("%s %d %s\r\n" %
|
|
| 382 | (self.protocol_version, code, message)) |
|
| 383 | # print (self.protocol_version, code, message) |
|
| 384 | self.send_header('Server', self.version_string())
|
|
| 385 | self.send_header('Date', self.date_time_string())
|
|
| 386 | ||
| 2e-06 sec | 387 | def send_header(self, keyword, value): |
| 388 | """Send a MIME header.""" |
|
| 389 | if self.request_version != 'HTTP/0.9': |
|
| 390 | self.wfile.write("%s: %s\r\n" % (keyword, value))
|
|
| 391 | ||
| 392 | if keyword.lower() == 'connection': |
|
| 393 | if value.lower() == 'close': |
|
| 394 | self.close_connection = 1 |
|
| 395 | elif value.lower() == 'keep-alive': |
|
| 396 | self.close_connection = 0 |
|
| 397 | ||
| 2e-06 sec | 398 | def end_headers(self): |
| 399 | """Send the blank line ending the MIME headers.""" |
|
| 400 | if self.request_version != 'HTTP/0.9': |
|
| 401 | self.wfile.write("\r\n")
|
|
| 402 | ||
| 3e-06 sec | 403 | def log_request(self, code='-', size='-'): |
| 404 | """Log an accepted request. |
|
| 405 | ||
| 406 | This is called by send_response(). |
|
| 407 | ||
| 408 | """ |
|
| 409 | ||
| 410 | self.log_message('"%s" %s %s',
|
|
| 411 | self.requestline, str(code), str(size)) |
|
| 412 | ||
| 3e-06 sec | 413 | def log_error(self, format, *args): |
| 414 | """Log an error. |
|
| 415 | ||
| 416 | This is called when a request cannot be fulfilled. By |
|
| 417 | default it passes the message on to log_message(). |
|
| 418 | ||
| 419 | Arguments are the same as for log_message(). |
|
| 420 | ||
| 421 | XXX This should go to the separate error log. |
|
| 422 | ||
| 423 | """ |
|
| 424 | ||
| 425 | self.log_message(format, *args) |
|
| 426 | ||
| 2e-06 sec | 427 | def log_message(self, format, *args): |
| 428 | """Log an arbitrary message. |
|
| 429 | ||
| 430 | This is used by all other logging functions. Override |
|
| 431 | it if you have specific logging wishes. |
|
| 432 | ||
| 433 | The first argument, FORMAT, is a format string for the |
|
| 434 | message to be logged. If the format string contains |
|
| 435 | any % escapes requiring parameters, they should be |
|
| 436 | specified as subsequent arguments (it's just like |
|
| 437 | printf!). |
|
| 438 | ||
| 439 | The client host and current date/time are prefixed to |
|
| 440 | every message. |
|
| 441 | ||
| 442 | """ |
|
| 443 | ||
| 444 | sys.stderr.write("%s - - [%s] %s\n" %
|
|
| 445 | (self.address_string(), |
|
| 446 | self.log_date_time_string(), |
|
| 447 | format%args)) |
|
| 448 | ||
| 2e-06 sec | 449 | def version_string(self): |
| 450 | """Return the server software version string.""" |
|
| 451 | return self.server_version + ' ' + self.sys_version |
|
| 452 | ||
| 3e-06 sec | 453 | def date_time_string(self, timestamp=None): |
| 454 | """Return the current date and time formatted for a message header.""" |
|
| 455 | if timestamp is None: |
|
| 456 | timestamp = time.time() |
|
| 457 | year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) |
|
| 458 | s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( |
|
| 459 | self.weekdayname[wd], |
|
| 460 | day, self.monthname[month], year, |
|
| 461 | hh, mm, ss) |
|
| 462 | return s |
|
| 463 | ||
| 2e-06 sec | 464 | def log_date_time_string(self): |
| 465 | """Return the current time formatted for logging.""" |
|
| 466 | now = time.time() |
|
| 467 | year, month, day, hh, mm, ss, x, y, z = time.localtime(now) |
|
| 468 | s = "%02d/%3s/%04d %02d:%02d:%02d" % ( |
|
| 469 | day, self.monthname[month], year, hh, mm, ss) |
|
| 470 | return s |
|
| 471 | ||
| 3e-06 sec | 472 | weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] |
| 473 | ||
| 3e-06 sec | 474 | monthname = [None, |
| 2e-06 sec | 475 | 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', |
| 2e-06 sec | 476 | 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] |
| 477 | ||
| 6e-06 sec | 478 | def address_string(self): |
| 479 | """Return the client address formatted for logging. |
|
| 480 | ||
| 481 | This version looks up the full hostname using gethostbyaddr(), |
|
| 482 | and tries to find a name that contains at least one dot. |
|
| 483 | ||
| 484 | """ |
|
| 485 | ||
| 486 | host, port = self.client_address[:2] |
|
| 487 | return socket.getfqdn(host) |
|
| 488 | ||
| 489 | # Essentially static class variables |
|
| 490 | ||
| 491 | # The version of the HTTP protocol we support. |
|
| 492 | # Set this to HTTP/1.1 to enable automatic keepalive |
|
| 3e-06 sec | 493 | protocol_version = "HTTP/1.0" |
| 494 | ||
| 495 | # The Message-like class used to parse headers |
|
| 3e-06 sec | 496 | MessageClass = mimetools.Message |
| 497 | ||
| 498 | # Table mapping response codes to messages; entries have the |
|
| 499 | # form {code: (shortmessage, longmessage)}.
|
|
| 500 | # See RFC 2616. |
|
| 4e-06 sec | 501 | responses = {
|
| 3e-06 sec | 502 | 100: ('Continue', 'Request received, please continue'),
|
| 503 | 101: ('Switching Protocols',
|
|
| 4e-06 sec | 504 | 'Switching to new protocol; obey Upgrade header'), |
| 505 | ||
| 2e-06 sec | 506 | 200: ('OK', 'Request fulfilled, document follows'),
|
| 2e-06 sec | 507 | 201: ('Created', 'Document created, URL follows'),
|
| 508 | 202: ('Accepted',
|
|
| 2e-06 sec | 509 | 'Request accepted, processing continues off-line'), |
| 2e-06 sec | 510 | 203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
|
| 2e-06 sec | 511 | 204: ('No Content', 'Request fulfilled, nothing follows'),
|
| 2e-06 sec | 512 | 205: ('Reset Content', 'Clear input form for further input.'),
|
| 2e-06 sec | 513 | 206: ('Partial Content', 'Partial content follows.'),
|
| 514 | ||
| 515 | 300: ('Multiple Choices',
|
|
| 2e-06 sec | 516 | 'Object has several resources -- see URI list'), |
| 2e-06 sec | 517 | 301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
|
| 2e-06 sec | 518 | 302: ('Found', 'Object moved temporarily -- see URI list'),
|
| 3e-06 sec | 519 | 303: ('See Other', 'Object moved -- see Method and URL list'),
|
| 520 | 304: ('Not Modified',
|
|
| 2e-06 sec | 521 | 'Document has not changed since given time'), |
| 522 | 305: ('Use Proxy',
|
|
| 2e-06 sec | 523 | 'You must use proxy specified in Location to access this ' |
| 524 | 'resource.'), |
|
| 525 | 307: ('Temporary Redirect',
|
|
| 2e-06 sec | 526 | 'Object moved temporarily -- see URI list'), |
| 527 | ||
| 528 | 400: ('Bad Request',
|
|
| 2e-06 sec | 529 | 'Bad request syntax or unsupported method'), |
| 530 | 401: ('Unauthorized',
|
|
| 2e-06 sec | 531 | 'No permission -- see authorization schemes'), |
| 532 | 402: ('Payment Required',
|
|
| 3e-06 sec | 533 | 'No payment -- see charging schemes'), |
| 534 | 403: ('Forbidden',
|
|
| 1e-06 sec | 535 | 'Request forbidden -- authorization will not help'), |
| 3e-06 sec | 536 | 404: ('Not Found', 'Nothing matches the given URI'),
|
| 537 | 405: ('Method Not Allowed',
|
|
| 2e-06 sec | 538 | 'Specified method is invalid for this server.'), |
| 2e-06 sec | 539 | 406: ('Not Acceptable', 'URI not available in preferred format.'),
|
| 3e-06 sec | 540 | 407: ('Proxy Authentication Required', 'You must authenticate with '
|
| 541 | 'this proxy before proceeding.'), |
|
| 3e-06 sec | 542 | 408: ('Request Timeout', 'Request timed out; try again later.'),
|
| 2e-06 sec | 543 | 409: ('Conflict', 'Request conflict.'),
|
| 544 | 410: ('Gone',
|
|
| 2e-06 sec | 545 | 'URI no longer exists and has been permanently removed.'), |
| 3e-06 sec | 546 | 411: ('Length Required', 'Client must specify Content-Length.'),
|
| 2e-06 sec | 547 | 412: ('Precondition Failed', 'Precondition in headers is false.'),
|
| 2e-06 sec | 548 | 413: ('Request Entity Too Large', 'Entity is too large.'),
|
| 2e-06 sec | 549 | 414: ('Request-URI Too Long', 'URI is too long.'),
|
| 3e-06 sec | 550 | 415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
|
| 551 | 416: ('Requested Range Not Satisfiable',
|
|
| 2e-06 sec | 552 | 'Cannot satisfy request range.'), |
| 553 | 417: ('Expectation Failed',
|
|
| 2e-06 sec | 554 | 'Expect condition could not be satisfied.'), |
| 555 | ||
| 3e-06 sec | 556 | 500: ('Internal Server Error', 'Server got itself in trouble'),
|
| 557 | 501: ('Not Implemented',
|
|
| 2e-06 sec | 558 | 'Server does not support this operation'), |
| 2e-06 sec | 559 | 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
|
| 560 | 503: ('Service Unavailable',
|
|
| 3e-06 sec | 561 | 'The server cannot process the request due to a high load'), |
| 562 | 504: ('Gateway Timeout',
|
|
| 2e-06 sec | 563 | 'The gateway server did not receive a timely response'), |
| 2e-06 sec | 564 | 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
|
| 565 | } |
|
| 566 | ||
| 567 | ||
| 9e-06 sec | 568 | def test(HandlerClass = BaseHTTPRequestHandler, |
| 2e-06 sec | 569 | ServerClass = HTTPServer, protocol="HTTP/1.0"): |
| 570 | """Test the HTTP request handler class. |
|
| 571 | ||
| 572 | This runs an HTTP server on port 8000 (or the first command line |
|
| 573 | argument). |
|
| 574 | ||
| 575 | """ |
|
| 576 | ||
| 577 | if sys.argv[1:]: |
|
| 578 | port = int(sys.argv[1]) |
|
| 579 | else: |
|
| 580 | port = 8000 |
|
| 581 | server_address = ('', port)
|
|
| 582 | ||
| 583 | HandlerClass.protocol_version = protocol |
|
| 584 | httpd = ServerClass(server_address, HandlerClass) |
|
| 585 | ||
| 586 | sa = httpd.socket.getsockname() |
|
| 587 | print "Serving HTTP on", sa[0], "port", sa[1], "..." |
|
| 588 | httpd.serve_forever() |
|
| 589 | ||
| 590 | ||
| 3e-06 sec | 591 | if __name__ == '__main__': |
| 592 | test() |
|
| /usr/lib/python2.6/Queue.py | ||
|---|---|---|
| time | num | code |
| 0.000698 sec | 1 | """A multi-producer, multi-consumer queue.""" |
| 2 | ||
| 2e-06 sec | 3 | from time import time as _time |
| 1.9e-05 sec | 4 | from collections import deque |
| 1e-05 sec | 5 | import heapq |
| 6 | ||
| 2.5e-05 sec | 7 | __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue'] |
| 8 | ||
| 1.3e-05 sec | 9 | class Empty(Exception): |
| 2e-06 sec | 10 | "Exception raised by Queue.get(block=0)/get_nowait()." |
| 2e-06 sec | 11 | pass |
| 12 | ||
| 0.00012 sec | 13 | class Full(Exception): |
| 2e-06 sec | 14 | "Exception raised by Queue.put(block=0)/put_nowait()." |
| 2e-06 sec | 15 | pass |
| 16 | ||
| 7.4e-05 sec | 17 | class Queue: |
| 18 | """Create a queue object with a given maximum size. |
|
| 19 | ||
| 20 | If maxsize is <= 0, the queue size is infinite. |
|
| 1e-06 sec | 21 | """ |
| 1.3e-05 sec | 22 | def __init__(self, maxsize=0): |
| 1e-06 sec | 23 | try: |
| 2e-06 sec | 24 | import threading |
| 25 | except ImportError: |
|
| 26 | import dummy_threading as threading |
|
| 8e-06 sec | 27 | self.maxsize = maxsize |
| 3e-06 sec | 28 | self._init(maxsize) |
| 29 | # mutex must be held whenever the queue is mutating. All methods |
|
| 30 | # that acquire mutex must release it before returning. mutex |
|
| 31 | # is shared between the three conditions, so acquiring and |
|
| 32 | # releasing the conditions also acquires and releases mutex. |
|
| 2e-06 sec | 33 | self.mutex = threading.Lock() |
| 34 | # Notify not_empty whenever an item is added to the queue; a |
|
| 35 | # thread waiting to get is notified then. |
|
| 6e-06 sec | 36 | self.not_empty = threading.Condition(self.mutex) |
| 37 | # Notify not_full whenever an item is removed from the queue; |
|
| 38 | # a thread waiting to put is notified then. |
|
| 3e-06 sec | 39 | self.not_full = threading.Condition(self.mutex) |
| 40 | # Notify all_tasks_done whenever the number of unfinished tasks |
|
| 41 | # drops to zero; thread waiting to join() is notified to resume |
|
| 2e-06 sec | 42 | self.all_tasks_done = threading.Condition(self.mutex) |
| 4e-06 sec | 43 | self.unfinished_tasks = 0 |
| 44 | ||
| 3e-06 sec | 45 | def task_done(self): |
| 46 | """Indicate that a formerly enqueued task is complete. |
|
| 47 | ||
| 48 | Used by Queue consumer threads. For each get() used to fetch a task, |
|
| 49 | a subsequent call to task_done() tells the queue that the processing |
|
| 50 | on the task is complete. |
|
| 51 | ||
| 52 | If a join() is currently blocking, it will resume when all items |
|
| 53 | have been processed (meaning that a task_done() call was received |
|
| 54 | for every item that had been put() into the queue). |
|
| 55 | ||
| 56 | Raises a ValueError if called more times than there were items |
|
| 57 | placed in the queue. |
|
| 58 | """ |
|
| 59 | self.all_tasks_done.acquire() |
|
| 60 | try: |
|
| 61 | unfinished = self.unfinished_tasks - 1 |
|
| 62 | if unfinished <= 0: |
|
| 63 | if unfinished < 0: |
|
| 64 | raise ValueError('task_done() called too many times')
|
|
| 65 | self.all_tasks_done.notify_all() |
|
| 66 | self.unfinished_tasks = unfinished |
|
| 67 | finally: |
|
| 68 | self.all_tasks_done.release() |
|
| 69 | ||
| 2e-06 sec | 70 | def join(self): |
| 71 | """Blocks until all items in the Queue have been gotten and processed. |
|
| 72 | ||
| 73 | The count of unfinished tasks goes up whenever an item is added to the |
|
| 74 | queue. The count goes down whenever a consumer thread calls task_done() |
|
| 75 | to indicate the item was retrieved and all work on it is complete. |
|
| 76 | ||
| 77 | When the count of unfinished tasks drops to zero, join() unblocks. |
|
| 78 | """ |
|
| 79 | self.all_tasks_done.acquire() |
|
| 80 | try: |
|
| 81 | while self.unfinished_tasks: |
|
| 82 | self.all_tasks_done.wait() |
|
| 83 | finally: |
|
| 84 | self.all_tasks_done.release() |
|
| 85 | ||
| 2e-06 sec | 86 | def qsize(self): |
| 87 | """Return the approximate size of the queue (not reliable!).""" |
|
| 88 | self.mutex.acquire() |
|
| 89 | n = self._qsize() |
|
| 90 | self.mutex.release() |
|
| 91 | return n |
|
| 92 | ||
| 4e-06 sec | 93 | def empty(self): |
| 94 | """Return True if the queue is empty, False otherwise (not reliable!).""" |
|
| 95 | self.mutex.acquire() |
|
| 96 | n = not self._qsize() |
|
| 97 | self.mutex.release() |
|
| 98 | return n |
|
| 99 | ||
| 3e-06 sec | 100 | def full(self): |
| 101 | """Return True if the queue is full, False otherwise (not reliable!).""" |
|
| 102 | self.mutex.acquire() |
|
| 103 | n = 0 < self.maxsize == self._qsize() |
|
| 104 | self.mutex.release() |
|
| 105 | return n |
|
| 106 | ||
| 8.6e-05 sec | 107 | def put(self, item, block=True, timeout=None): |
| 108 | """Put an item into the queue. |
|
| 109 | ||
| 110 | If optional args 'block' is true and 'timeout' is None (the default), |
|
| 111 | block if necessary until a free slot is available. If 'timeout' is |
|
| 112 | a positive number, it blocks at most 'timeout' seconds and raises |
|
| 113 | the Full exception if no free slot was available within that time. |
|
| 114 | Otherwise ('block' is false), put an item on the queue if a free slot
|
|
| 115 | is immediately available, else raise the Full exception ('timeout'
|
|
| 116 | is ignored in that case). |
|
| 117 | """ |
|
| 1.8e-05 sec | 118 | self.not_full.acquire() |
| 4.5e-05 sec | 119 | try: |
| 2e-05 sec | 120 | if self.maxsize > 0: |
| 121 | if not block: |
|
| 122 | if self._qsize() == self.maxsize: |
|
| 123 | raise Full |
|
| 124 | elif timeout is None: |
|
| 125 | while self._qsize() == self.maxsize: |
|
| 126 | self.not_full.wait() |
|
| 127 | elif timeout < 0: |
|
| 128 | raise ValueError("'timeout' must be a positive number")
|
|
| 129 | else: |
|
| 130 | endtime = _time() + timeout |
|
| 131 | while self._qsize() == self.maxsize: |
|
| 132 | remaining = endtime - _time() |
|
| 133 | if remaining <= 0.0: |
|
| 134 | raise Full |
|
| 135 | self.not_full.wait(remaining) |
|
| 3e-05 sec | 136 | self._put(item) |
| 2.2e-05 sec | 137 | self.unfinished_tasks += 1 |
| 3.5e-05 sec | 138 | self.not_empty.notify() |
| 139 | finally: |
|
| 2.7e-05 sec | 140 | self.not_full.release() |
| 141 | ||
| 3e-06 sec | 142 | def put_nowait(self, item): |
| 143 | """Put an item into the queue without blocking. |
|
| 144 | ||
| 145 | Only enqueue the item if a free slot is immediately available. |
|
| 146 | Otherwise raise the Full exception. |
|
| 147 | """ |
|
| 148 | return self.put(item, False) |
|
| 149 | ||
| 2e-06 sec | 150 | def get(self, block=True, timeout=None): |
| 151 | """Remove and return an item from the queue. |
|
| 152 | ||
| 153 | If optional args 'block' is true and 'timeout' is None (the default), |
|
| 154 | block if necessary until an item is available. If 'timeout' is |
|
| 155 | a positive number, it blocks at most 'timeout' seconds and raises |
|
| 156 | the Empty exception if no item was available within that time. |
|
| 157 | Otherwise ('block' is false), return an item if one is immediately
|
|
| 158 | available, else raise the Empty exception ('timeout' is ignored
|
|
| 159 | in that case). |
|
| 160 | """ |
|
| 161 | self.not_empty.acquire() |
|
| 162 | try: |
|
| 163 | if not block: |
|
| 164 | if not self._qsize(): |
|
| 165 | raise Empty |
|
| 166 | elif timeout is None: |
|
| 167 | while not self._qsize(): |
|
| 168 | self.not_empty.wait() |
|
| 169 | elif timeout < 0: |
|
| 170 | raise ValueError("'timeout' must be a positive number")
|
|
| 171 | else: |
|
| 172 | endtime = _time() + timeout |
|
| 173 | while not self._qsize(): |
|
| 174 | remaining = endtime - _time() |
|
| 175 | if remaining <= 0.0: |
|
| 176 | raise Empty |
|
| 177 | self.not_empty.wait(remaining) |
|
| 178 | item = self._get() |
|
| 179 | self.not_full.notify() |
|
| 180 | return item |
|
| 181 | finally: |
|
| 182 | self.not_empty.release() |
|
| 183 | ||
| 3e-06 sec | 184 | def get_nowait(self): |
| 185 | """Remove and return an item from the queue without blocking. |
|
| 186 | ||
| 187 | Only get an item if one is immediately available. Otherwise |
|
| 188 | raise the Empty exception. |
|
| 189 | """ |
|
| 190 | return self.get(False) |
|
| 191 | ||
| 192 | # Override these methods to implement other queue organizations |
|
| 193 | # (e.g. stack or priority queue). |
|
| 194 | # These will only be called with appropriate locks held |
|
| 195 | ||
| 196 | # Initialize the queue representation |
|
| 1.7e-05 sec | 197 | def _init(self, maxsize): |
| 2e-06 sec | 198 | self.queue = deque() |
| 199 | ||
| 3e-06 sec | 200 | def _qsize(self, len=len): |
| 201 | return len(self.queue) |
|
| 202 | ||
| 203 | # Put a new item in the queue |
|
| 7.8e-05 sec | 204 | def _put(self, item): |
| 2.1e-05 sec | 205 | self.queue.append(item) |
| 206 | ||
| 207 | # Get an item from the queue |
|
| 2e-06 sec | 208 | def _get(self): |
| 209 | return self.queue.popleft() |
|
| 210 | ||
| 211 | ||
| 1.2e-05 sec | 212 | class PriorityQueue(Queue): |
| 213 | '''Variant of Queue that retrieves open entries in priority order (lowest first). |
|
| 214 | ||
| 215 | Entries are typically tuples of the form: (priority number, data). |
|
| 2e-06 sec | 216 | ''' |
| 217 | ||
| 1e-06 sec | 218 | def _init(self, maxsize): |
| 219 | self.queue = [] |
|
| 220 | ||
| 3e-06 sec | 221 | def _qsize(self, len=len): |
| 222 | return len(self.queue) |
|
| 223 | ||
| 2e-06 sec | 224 | def _put(self, item, heappush=heapq.heappush): |
| 225 | heappush(self.queue, item) |
|
| 226 | ||
| 3e-06 sec | 227 | def _get(self, heappop=heapq.heappop): |
| 228 | return heappop(self.queue) |
|
| 229 | ||
| 230 | ||
| 1e-05 sec | 231 | class LifoQueue(Queue): |
| 2e-06 sec | 232 | '''Variant of Queue that retrieves most recently added entries first.''' |
| 233 | ||
| 3e-06 sec | 234 | def _init(self, maxsize): |
| 235 | self.queue = [] |
|
| 236 | ||
| 2e-06 sec | 237 | def _qsize(self, len=len): |
| 238 | return len(self.queue) |
|
| 239 | ||
| 3e-06 sec | 240 | def _put(self, item): |
| 241 | self.queue.append(item) |
|
| 242 | ||
| 2e-06 sec | 243 | def _get(self): |
| 244 | return self.queue.pop() |
|
| /usr/lib/python2.6/SimpleHTTPServer.py | ||
|---|---|---|
| time | num | code |
| 1 | """Simple HTTP Server. |
|
| 2 | ||
| 3 | This module builds on BaseHTTPServer by implementing the standard GET |
|
| 4 | and HEAD requests in a fairly straightforward manner. |
|
| 5 | ||
| 0.000705 sec | 6 | """ |
| 7 | ||
| 8 | ||
| 2e-06 sec | 9 | __version__ = "0.6" |
| 10 | ||
| 4e-06 sec | 11 | __all__ = ["SimpleHTTPRequestHandler"] |
| 12 | ||
| 2e-06 sec | 13 | import os |
| 9e-06 sec | 14 | import posixpath |
| 5e-06 sec | 15 | import BaseHTTPServer |
| 2e-05 sec | 16 | import urllib |
| 6e-06 sec | 17 | import cgi |
| 6e-06 sec | 18 | import shutil |
| 2e-05 sec | 19 | import mimetypes |
| 1.6e-05 sec | 20 | try: |
| 2e-06 sec | 21 | from cStringIO import StringIO |
| 22 | except ImportError: |
|
| 23 | from StringIO import StringIO |
|
| 24 | ||
| 25 | ||
| 1.7e-05 sec | 26 | class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
| 27 | ||
| 28 | """Simple HTTP request handler with GET and HEAD commands. |
|
| 29 | ||
| 30 | This serves files from the current directory and any of its |
|
| 31 | subdirectories. The MIME type for files is determined by |
|
| 32 | calling the .guess_type() method. |
|
| 33 | ||
| 34 | The GET and HEAD requests are identical except that the HEAD |
|
| 35 | request omits the actual contents of the file. |
|
| 36 | ||
| 2e-06 sec | 37 | """ |
| 38 | ||
| 2e-06 sec | 39 | server_version = "SimpleHTTP/" + __version__ |
| 40 | ||
| 5e-06 sec | 41 | def do_GET(self): |
| 42 | """Serve a GET request.""" |
|
| 43 | f = self.send_head() |
|
| 44 | if f: |
|
| 45 | self.copyfile(f, self.wfile) |
|
| 46 | f.close() |
|
| 47 | ||
| 2e-06 sec | 48 | def do_HEAD(self): |
| 49 | """Serve a HEAD request.""" |
|
| 50 | f = self.send_head() |
|
| 51 | if f: |
|
| 52 | f.close() |
|
| 53 | ||
| 3e-06 sec | 54 | def send_head(self): |
| 55 | """Common code for GET and HEAD commands. |
|
| 56 | ||
| 57 | This sends the response code and MIME headers. |
|
| 58 | ||
| 59 | Return value is either a file object (which has to be copied |
|
| 60 | to the outputfile by the caller unless the command was HEAD, |
|
| 61 | and must be closed by the caller under all circumstances), or |
|
| 62 | None, in which case the caller has nothing further to do. |
|
| 63 | ||
| 64 | """ |
|
| 65 | path = self.translate_path(self.path) |
|
| 66 | f = None |
|
| 67 | if os.path.isdir(path): |
|
| 68 | if not self.path.endswith('/'):
|
|
| 69 | # redirect browser - doing basically what apache does |
|
| 70 | self.send_response(301) |
|
| 71 | self.send_header("Location", self.path + "/")
|
|
| 72 | self.end_headers() |
|
| 73 | return None |
|
| 74 | for index in "index.html", "index.htm": |
|
| 75 | index = os.path.join(path, index) |
|
| 76 | if os.path.exists(index): |
|
| 77 | path = index |
|
| 78 | break |
|
| 79 | else: |
|
| 80 | return self.list_directory(path) |
|
| 81 | ctype = self.guess_type(path) |
|
| 82 | try: |
|
| 83 | # Always read in binary mode. Opening files in text mode may cause |
|
| 84 | # newline translations, making the actual size of the content |
|
| 85 | # transmitted *less* than the content-length! |
|
| 86 | f = open(path, 'rb') |
|
| 87 | except IOError: |
|
| 88 | self.send_error(404, "File not found") |
|
| 89 | return None |
|
| 90 | self.send_response(200) |
|
| 91 | self.send_header("Content-type", ctype)
|
|
| 92 | fs = os.fstat(f.fileno()) |
|
| 93 | self.send_header("Content-Length", str(fs[6]))
|
|
| 94 | self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
|
|
| 95 | self.end_headers() |
|
| 96 | return f |
|
| 97 | ||
| 3e-06 sec | 98 | def list_directory(self, path): |
| 99 | """Helper to produce a directory listing (absent index.html). |
|
| 100 | ||
| 101 | Return value is either a file object, or None (indicating an |
|
| 102 | error). In either case, the headers are sent, making the |
|
| 103 | interface the same as for send_head(). |
|
| 104 | ||
| 105 | """ |
|
| 106 | try: |
|
| 107 | list = os.listdir(path) |
|
| 108 | except os.error: |
|
| 109 | self.send_error(404, "No permission to list directory") |
|
| 110 | return None |
|
| 111 | list.sort(key=lambda a: a.lower()) |
|
| 112 | f = StringIO() |
|
| 113 | displaypath = cgi.escape(urllib.unquote(self.path)) |
|
| 114 | f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
|
|
| 115 | f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
|
|
| 116 | f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
|
|
| 117 | f.write("<hr>\n<ul>\n")
|
|
| 118 | for name in list: |
|
| 119 | fullname = os.path.join(path, name) |
|
| 120 | displayname = linkname = name |
|
| 121 | # Append / for directories or @ for symbolic links |
|
| 122 | if os.path.isdir(fullname): |
|
| 123 | displayname = name + "/" |
|
| 124 | linkname = name + "/" |
|
| 125 | if os.path.islink(fullname): |
|
| 126 | displayname = name + "@" |
|
| 127 | # Note: a link to a directory displays with @ and links with / |
|
| 128 | f.write('<li><a href="%s">%s</a>\n'
|
|
| 129 | % (urllib.quote(linkname), cgi.escape(displayname))) |
|
| 130 | f.write("</ul>\n<hr>\n</body>\n</html>\n")
|
|
| 131 | length = f.tell() |
|
| 132 | f.seek(0) |
|
| 133 | self.send_response(200) |
|
| 134 | self.send_header("Content-type", "text/html")
|
|
| 135 | self.send_header("Content-Length", str(length))
|
|
| 136 | self.end_headers() |
|
| 137 | return f |
|
| 138 | ||
| 2e-06 sec | 139 | def translate_path(self, path): |
| 140 | """Translate a /-separated PATH to the local filename syntax. |
|
| 141 | ||
| 142 | Components that mean special things to the local file system |
|
| 143 | (e.g. drive or directory names) are ignored. (XXX They should |
|
| 144 | probably be diagnosed.) |
|
| 145 | ||
| 146 | """ |
|
| 147 | # abandon query parameters |
|
| 148 | path = path.split('?',1)[0]
|
|
| 149 | path = path.split('#',1)[0]
|
|
| 150 | path = posixpath.normpath(urllib.unquote(path)) |
|
| 151 | words = path.split('/')
|
|
| 152 | words = filter(None, words) |
|
| 153 | path = os.getcwd() |
|
| 154 | for word in words: |
|
| 155 | drive, word = os.path.splitdrive(word) |
|
| 156 | head, word = os.path.split(word) |
|
| 157 | if word in (os.curdir, os.pardir): continue |
|
| 158 | path = os.path.join(path, word) |
|
| 159 | return path |
|
| 160 | ||
| 3e-06 sec | 161 | def copyfile(self, source, outputfile): |
| 162 | """Copy all data between two file objects. |
|
| 163 | ||
| 164 | The SOURCE argument is a file object open for reading |
|
| 165 | (or anything with a read() method) and the DESTINATION |
|
| 166 | argument is a file object open for writing (or |
|
| 167 | anything with a write() method). |
|
| 168 | ||
| 169 | The only reason for overriding this would be to change |
|
| 170 | the block size or perhaps to replace newlines by CRLF |
|
| 171 | -- note however that this the default server uses this |
|
| 172 | to copy binary data as well. |
|
| 173 | ||
| 174 | """ |
|
| 175 | shutil.copyfileobj(source, outputfile) |
|
| 176 | ||
| 2e-06 sec | 177 | def guess_type(self, path): |
| 178 | """Guess the type of a file. |
|
| 179 | ||
| 180 | Argument is a PATH (a filename). |
|
| 181 | ||
| 182 | Return value is a string of the form type/subtype, |
|
| 183 | usable for a MIME Content-type header. |
|
| 184 | ||
| 185 | The default implementation looks the file's extension |
|
| 186 | up in the table self.extensions_map, using application/octet-stream |
|
| 187 | as a default; however it would be permissible (if |
|
| 188 | slow) to look inside the data to make a better guess. |
|
| 189 | ||
| 190 | """ |
|
| 191 | ||
| 192 | base, ext = posixpath.splitext(path) |
|
| 193 | if ext in self.extensions_map: |
|
| 194 | return self.extensions_map[ext] |
|
| 195 | ext = ext.lower() |
|
| 196 | if ext in self.extensions_map: |
|
| 197 | return self.extensions_map[ext] |
|
| 198 | else: |
|
| 199 | return self.extensions_map[''] |
|
| 200 | ||
| 2e-06 sec | 201 | if not mimetypes.inited: |
| 3e-06 sec | 202 | mimetypes.init() # try to read system mime.types |
| 4e-06 sec | 203 | extensions_map = mimetypes.types_map.copy() |
| 9.5e-05 sec | 204 | extensions_map.update({
|
| 4e-06 sec | 205 | '': 'application/octet-stream', # Default |
| 2e-06 sec | 206 | '.py': 'text/plain', |
| 2e-06 sec | 207 | '.c': 'text/plain', |
| 3e-06 sec | 208 | '.h': 'text/plain', |
| 209 | }) |
|
| 210 | ||
| 211 | ||
| 2.1e-05 sec | 212 | def test(HandlerClass = SimpleHTTPRequestHandler, |
| 2e-06 sec | 213 | ServerClass = BaseHTTPServer.HTTPServer): |
| 214 | BaseHTTPServer.test(HandlerClass, ServerClass) |
|
| 215 | ||
| 216 | ||
| 6e-06 sec | 217 | if __name__ == '__main__': |
| 218 | test() |
|
| /usr/lib/python2.6/SocketServer.py | ||
|---|---|---|
| time | num | code |
| 1 | """Generic socket server classes. |
|
| 2 | ||
| 3 | This module tries to capture the various aspects of defining a server: |
|
| 4 | ||
| 5 | For socket-based servers: |
|
| 6 | ||
| 7 | - address family: |
|
| 8 | - AF_INET{,6}: IP (Internet Protocol) sockets (default)
|
|
| 9 | - AF_UNIX: Unix domain sockets |
|
| 10 | - others, e.g. AF_DECNET are conceivable (see <socket.h> |
|
| 11 | - socket type: |
|
| 12 | - SOCK_STREAM (reliable stream, e.g. TCP) |
|
| 13 | - SOCK_DGRAM (datagrams, e.g. UDP) |
|
| 14 | ||
| 15 | For request-based servers (including socket-based): |
|
| 16 | ||
| 17 | - client address verification before further looking at the request |
|
| 18 | (This is actually a hook for any processing that needs to look |
|
| 19 | at the request before anything else, e.g. logging) |
|
| 20 | - how to handle multiple requests: |
|
| 21 | - synchronous (one request is handled at a time) |
|
| 22 | - forking (each request is handled by a new process) |
|
| 23 | - threading (each request is handled by a new thread) |
|
| 24 | ||
| 25 | The classes in this module favor the server type that is simplest to |
|
| 26 | write: a synchronous TCP/IP server. This is bad class design, but |
|
| 27 | save some typing. (There's also the issue that a deep class hierarchy |
|
| 28 | slows down method lookups.) |
|
| 29 | ||
| 30 | There are five classes in an inheritance diagram, four of which represent |
|
| 31 | synchronous servers of four types: |
|
| 32 | ||
| 33 | +------------+ |
|
| 34 | | BaseServer | |
|
| 35 | +------------+ |
|
| 36 | | |
|
| 37 | v |
|
| 38 | +-----------+ +------------------+ |
|
| 39 | | TCPServer |------->| UnixStreamServer | |
|
| 40 | +-----------+ +------------------+ |
|
| 41 | | |
|
| 42 | v |
|
| 43 | +-----------+ +--------------------+ |
|
| 44 | | UDPServer |------->| UnixDatagramServer | |
|
| 45 | +-----------+ +--------------------+ |
|
| 46 | ||
| 47 | Note that UnixDatagramServer derives from UDPServer, not from |
|
| 48 | UnixStreamServer -- the only difference between an IP and a Unix |
|
| 49 | stream server is the address family, which is simply repeated in both |
|
| 50 | unix server classes. |
|
| 51 | ||
| 52 | Forking and threading versions of each type of server can be created |
|
| 53 | using the ForkingMixIn and ThreadingMixIn mix-in classes. For |
|
| 54 | instance, a threading UDP server class is created as follows: |
|
| 55 | ||
| 56 | class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass |
|
| 57 | ||
| 58 | The Mix-in class must come first, since it overrides a method defined |
|
| 59 | in UDPServer! Setting the various member variables also changes |
|
| 60 | the behavior of the underlying server mechanism. |
|
| 61 | ||
| 62 | To implement a service, you must derive a class from |
|
| 63 | BaseRequestHandler and redefine its handle() method. You can then run |
|
| 64 | various versions of the service by combining one of the server classes |
|
| 65 | with your request handler class. |
|
| 66 | ||
| 67 | The request handler class must be different for datagram or stream |
|
| 68 | services. This can be hidden by using the request handler |
|
| 69 | subclasses StreamRequestHandler or DatagramRequestHandler. |
|
| 70 | ||
| 71 | Of course, you still have to use your head! |
|
| 72 | ||
| 73 | For instance, it makes no sense to use a forking server if the service |
|
| 74 | contains state in memory that can be modified by requests (since the |
|
| 75 | modifications in the child process would never reach the initial state |
|
| 76 | kept in the parent process and passed to each child). In this case, |
|
| 77 | you can use a threading server, but you will probably have to use |
|
| 78 | locks to avoid two requests that come in nearly simultaneous to apply |
|
| 79 | conflicting changes to the server state. |
|
| 80 | ||
| 81 | On the other hand, if you are building e.g. an HTTP server, where all |
|
| 82 | data is stored externally (e.g. in the file system), a synchronous |
|
| 83 | class will essentially render the service "deaf" while one request is |
|
| 84 | being handled -- which may be for a very long time if a client is slow |
|
| 85 | to reqd all the data it has requested. Here a threading or forking |
|
| 86 | server is appropriate. |
|
| 87 | ||
| 88 | In some cases, it may be appropriate to process part of a request |
|
| 89 | synchronously, but to finish processing in a forked child depending on |
|
| 90 | the request data. This can be implemented by using a synchronous |
|
| 91 | server and doing an explicit fork in the request handler class |
|
| 92 | handle() method. |
|
| 93 | ||
| 94 | Another approach to handling multiple simultaneous requests in an |
|
| 95 | environment that supports neither threads nor fork (or where these are |
|
| 96 | too expensive or inappropriate for the service) is to maintain an |
|
| 97 | explicit table of partially finished requests and to use select() to |
|
| 98 | decide which request to work on next (or whether to handle a new |
|
| 99 | incoming request). This is particularly important for stream services |
|
| 100 | where each client can potentially be connected for a long time (if |
|
| 101 | threads or subprocesses cannot be used). |
|
| 102 | ||
| 103 | Future work: |
|
| 104 | - Standard classes for Sun RPC (which uses either UDP or TCP) |
|
| 105 | - Standard mix-in classes to implement various authentication |
|
| 106 | and encryption schemes |
|
| 107 | - Standard framework for select-based multiplexing |
|
| 108 | ||
| 109 | XXX Open problems: |
|
| 110 | - What to do with out-of-band data? |
|
| 111 | ||
| 112 | BaseServer: |
|
| 113 | - split generic "request" functionality out into BaseServer class. |
|
| 114 | Copyright (C) 2000 Luke Kenneth Casson Leighton <lkcl@samba.org> |
|
| 115 | ||
| 116 | example: read entries from a SQL database (requires overriding |
|
| 117 | get_request() to return a table entry from the database). |
|
| 118 | entry is processed by a RequestHandlerClass. |
|
| 119 | ||
| 0.000876 sec | 120 | """ |
| 121 | ||
| 122 | # Author of the BaseServer patch: Luke Kenneth Casson Leighton |
|
| 123 | ||
| 124 | # XXX Warning! |
|
| 125 | # There is a test suite for this module, but it cannot be run by the |
|
| 126 | # standard regression test. |
|
| 127 | # To run it manually, run Lib/test/test_socketserver.py. |
|
| 128 | ||
| 2e-06 sec | 129 | __version__ = "0.4" |
| 130 | ||
| 131 | ||
| 3e-06 sec | 132 | import socket |
| 8e-06 sec | 133 | import select |
| 4e-06 sec | 134 | import sys |
| 5e-06 sec | 135 | import os |
| 4e-06 sec | 136 | try: |
| 2e-06 sec | 137 | import threading |
| 138 | except ImportError: |
|
| 139 | import dummy_threading as threading |
|
| 140 | ||
| 5e-06 sec | 141 | __all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", |
| 2e-06 sec | 142 | "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler", |
| 2e-06 sec | 143 | "StreamRequestHandler","DatagramRequestHandler", |
| 2e-06 sec | 144 | "ThreadingMixIn", "ForkingMixIn"] |
| 3e-06 sec | 145 | if hasattr(socket, "AF_UNIX"): |
| 7e-06 sec | 146 | __all__.extend(["UnixStreamServer","UnixDatagramServer", |
| 3e-06 sec | 147 | "ThreadingUnixStreamServer", |
| 2e-06 sec | 148 | "ThreadingUnixDatagramServer"]) |
| 149 | ||
| 1.1e-05 sec | 150 | class BaseServer: |
| 151 | ||
| 152 | """Base class for server classes. |
|
| 153 | ||
| 154 | Methods for the caller: |
|
| 155 | ||
| 156 | - __init__(server_address, RequestHandlerClass) |
|
| 157 | - serve_forever(poll_interval=0.5) |
|
| 158 | - shutdown() |
|
| 159 | - handle_request() # if you do not use serve_forever() |
|
| 160 | - fileno() -> int # for select() |
|
| 161 | ||
| 162 | Methods that may be overridden: |
|
| 163 | ||
| 164 | - server_bind() |
|
| 165 | - server_activate() |
|
| 166 | - get_request() -> request, client_address |
|
| 167 | - handle_timeout() |
|
| 168 | - verify_request(request, client_address) |
|
| 169 | - server_close() |
|
| 170 | - process_request(request, client_address) |
|
| 171 | - close_request(request) |
|
| 172 | - handle_error() |
|
| 173 | ||
| 174 | Methods for derived classes: |
|
| 175 | ||
| 176 | - finish_request(request, client_address) |
|
| 177 | ||
| 178 | Class variables that may be overridden by derived classes or |
|
| 179 | instances: |
|
| 180 | ||
| 181 | - timeout |
|
| 182 | - address_family |
|
| 183 | - socket_type |
|
| 184 | - allow_reuse_address |
|
| 185 | ||
| 186 | Instance variables: |
|
| 187 | ||
| 188 | - RequestHandlerClass |
|
| 189 | - socket |
|
| 190 | ||
| 2e-06 sec | 191 | """ |
| 192 | ||
| 1e-06 sec | 193 | timeout = None |
| 194 | ||
| 2e-06 sec | 195 | def __init__(self, server_address, RequestHandlerClass): |
| 196 | """Constructor. May be extended, do not override.""" |
|
| 197 | self.server_address = server_address |
|
| 198 | self.RequestHandlerClass = RequestHandlerClass |
|
| 199 | self.__is_shut_down = threading.Event() |
|
| 200 | self.__serving = False |
|
| 201 | ||
| 2e-06 sec | 202 | def server_activate(self): |
| 203 | """Called by constructor to activate the server. |
|
| 204 | ||
| 205 | May be overridden. |
|
| 206 | ||
| 207 | """ |
|
| 208 | pass |
|
| 209 | ||
| 2e-06 sec | 210 | def serve_forever(self, poll_interval=0.5): |
| 211 | """Handle one request at a time until shutdown. |
|
| 212 | ||
| 213 | Polls for shutdown every poll_interval seconds. Ignores |
|
| 214 | self.timeout. If you need to do periodic tasks, do them in |
|
| 215 | another thread. |
|
| 216 | """ |
|
| 217 | self.__serving = True |
|
| 218 | self.__is_shut_down.clear() |
|
| 219 | while self.__serving: |
|
| 220 | # XXX: Consider using another file descriptor or |
|
| 221 | # connecting to the socket to wake this up instead of |
|
| 222 | # polling. Polling reduces our responsiveness to a |
|
| 223 | # shutdown request and wastes cpu at all other times. |
|
| 224 | r, w, e = select.select([self], [], [], poll_interval) |
|
| 225 | if r: |
|
| 226 | self._handle_request_noblock() |
|
| 227 | self.__is_shut_down.set() |
|
| 228 | ||
| 4e-06 sec | 229 | def shutdown(self): |
| 230 | """Stops the serve_forever loop. |
|
| 231 | ||
| 232 | Blocks until the loop has finished. This must be called while |
|
| 233 | serve_forever() is running in another thread, or it will |
|
| 234 | deadlock. |
|
| 235 | """ |
|
| 236 | self.__serving = False |
|
| 237 | self.__is_shut_down.wait() |
|
| 238 | ||
| 239 | # The distinction between handling, getting, processing and |
|
| 240 | # finishing a request is fairly arbitrary. Remember: |
|
| 241 | # |
|
| 242 | # - handle_request() is the top-level call. It calls |
|
| 243 | # select, get_request(), verify_request() and process_request() |
|
| 244 | # - get_request() is different for stream or datagram sockets |
|
| 245 | # - process_request() is the place that may fork a new process |
|
| 246 | # or create a new thread to finish the request |
|
| 247 | # - finish_request() instantiates the request handler class; |
|
| 248 | # this constructor will handle the request all by itself |
|
| 249 | ||
| 2e-06 sec | 250 | def handle_request(self): |
| 251 | """Handle one request, possibly blocking. |
|
| 252 | ||
| 253 | Respects self.timeout. |
|
| 254 | """ |
|
| 255 | # Support people who used socket.settimeout() to escape |
|
| 256 | # handle_request before self.timeout was available. |
|
| 257 | timeout = self.socket.gettimeout() |
|
| 258 | if timeout is None: |
|
| 259 | timeout = self.timeout |
|
| 260 | elif self.timeout is not None: |
|
| 261 | timeout = min(timeout, self.timeout) |
|
| 262 | fd_sets = select.select([self], [], [], timeout) |
|
| 263 | if not fd_sets[0]: |
|
| 264 | self.handle_timeout() |
|
| 265 | return |
|
| 266 | self._handle_request_noblock() |
|
| 267 | ||
| 3e-06 sec | 268 | def _handle_request_noblock(self): |
| 269 | """Handle one request, without blocking. |
|
| 270 | ||
| 271 | I assume that select.select has returned that the socket is |
|
| 272 | readable before this function was called, so there should be |
|
| 273 | no risk of blocking in get_request(). |
|
| 274 | """ |
|
| 275 | try: |
|
| 276 | request, client_address = self.get_request() |
|
| 277 | except socket.error: |
|
| 278 | return |
|
| 279 | if self.verify_request(request, client_address): |
|
| 280 | try: |
|
| 281 | self.process_request(request, client_address) |
|
| 282 | except: |
|
| 283 | self.handle_error(request, client_address) |
|
| 284 | self.close_request(request) |
|
| 285 | ||
| 2e-06 sec | 286 | def handle_timeout(self): |
| 287 | """Called if no new request arrives within self.timeout. |
|
| 288 | ||
| 289 | Overridden by ForkingMixIn. |
|
| 290 | """ |
|
| 291 | pass |
|
| 292 | ||
| 2e-06 sec | 293 | def verify_request(self, request, client_address): |
| 294 | """Verify the request. May be overridden. |
|
| 295 | ||
| 296 | ||