1*cda5da8dSAndroid Build Coastguard Worker"""HTTP server classes. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerNote: BaseHTTPRequestHandler doesn't implement any HTTP request; see 4*cda5da8dSAndroid Build Coastguard WorkerSimpleHTTPRequestHandler for simple implementations of GET, HEAD and POST, 5*cda5da8dSAndroid Build Coastguard Workerand CGIHTTPRequestHandler for CGI scripts. 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard WorkerIt does, however, optionally implement HTTP/1.1 persistent connections, 8*cda5da8dSAndroid Build Coastguard Workeras of version 0.3. 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard WorkerNotes on CGIHTTPRequestHandler 11*cda5da8dSAndroid Build Coastguard Worker------------------------------ 12*cda5da8dSAndroid Build Coastguard Worker 13*cda5da8dSAndroid Build Coastguard WorkerThis class implements GET and POST requests to cgi-bin scripts. 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard WorkerIf the os.fork() function is not present (e.g. on Windows), 16*cda5da8dSAndroid Build Coastguard Workersubprocess.Popen() is used as a fallback, with slightly altered semantics. 17*cda5da8dSAndroid Build Coastguard Worker 18*cda5da8dSAndroid Build Coastguard WorkerIn all cases, the implementation is intentionally naive -- all 19*cda5da8dSAndroid Build Coastguard Workerrequests are executed synchronously. 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard WorkerSECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL 22*cda5da8dSAndroid Build Coastguard Worker-- it may execute arbitrary Python code or external programs. 23*cda5da8dSAndroid Build Coastguard Worker 24*cda5da8dSAndroid Build Coastguard WorkerNote that status code 200 is sent prior to execution of a CGI script, so 25*cda5da8dSAndroid Build Coastguard Workerscripts cannot send other status codes such as 302 (redirect). 26*cda5da8dSAndroid Build Coastguard Worker 27*cda5da8dSAndroid Build Coastguard WorkerXXX To do: 28*cda5da8dSAndroid Build Coastguard Worker 29*cda5da8dSAndroid Build Coastguard Worker- log requests even later (to capture byte count) 30*cda5da8dSAndroid Build Coastguard Worker- log user-agent header and other interesting goodies 31*cda5da8dSAndroid Build Coastguard Worker- send error log to separate file 32*cda5da8dSAndroid Build Coastguard Worker""" 33*cda5da8dSAndroid Build Coastguard Worker 34*cda5da8dSAndroid Build Coastguard Worker 35*cda5da8dSAndroid Build Coastguard Worker# See also: 36*cda5da8dSAndroid Build Coastguard Worker# 37*cda5da8dSAndroid Build Coastguard Worker# HTTP Working Group T. Berners-Lee 38*cda5da8dSAndroid Build Coastguard Worker# INTERNET-DRAFT R. T. Fielding 39*cda5da8dSAndroid Build Coastguard Worker# <draft-ietf-http-v10-spec-00.txt> H. Frystyk Nielsen 40*cda5da8dSAndroid Build Coastguard Worker# Expires September 8, 1995 March 8, 1995 41*cda5da8dSAndroid Build Coastguard Worker# 42*cda5da8dSAndroid Build Coastguard Worker# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt 43*cda5da8dSAndroid Build Coastguard Worker# 44*cda5da8dSAndroid Build Coastguard Worker# and 45*cda5da8dSAndroid Build Coastguard Worker# 46*cda5da8dSAndroid Build Coastguard Worker# Network Working Group R. Fielding 47*cda5da8dSAndroid Build Coastguard Worker# Request for Comments: 2616 et al 48*cda5da8dSAndroid Build Coastguard Worker# Obsoletes: 2068 June 1999 49*cda5da8dSAndroid Build Coastguard Worker# Category: Standards Track 50*cda5da8dSAndroid Build Coastguard Worker# 51*cda5da8dSAndroid Build Coastguard Worker# URL: http://www.faqs.org/rfcs/rfc2616.html 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Worker# Log files 54*cda5da8dSAndroid Build Coastguard Worker# --------- 55*cda5da8dSAndroid Build Coastguard Worker# 56*cda5da8dSAndroid Build Coastguard Worker# Here's a quote from the NCSA httpd docs about log file format. 57*cda5da8dSAndroid Build Coastguard Worker# 58*cda5da8dSAndroid Build Coastguard Worker# | The logfile format is as follows. Each line consists of: 59*cda5da8dSAndroid Build Coastguard Worker# | 60*cda5da8dSAndroid Build Coastguard Worker# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb 61*cda5da8dSAndroid Build Coastguard Worker# | 62*cda5da8dSAndroid Build Coastguard Worker# | host: Either the DNS name or the IP number of the remote client 63*cda5da8dSAndroid Build Coastguard Worker# | rfc931: Any information returned by identd for this person, 64*cda5da8dSAndroid Build Coastguard Worker# | - otherwise. 65*cda5da8dSAndroid Build Coastguard Worker# | authuser: If user sent a userid for authentication, the user name, 66*cda5da8dSAndroid Build Coastguard Worker# | - otherwise. 67*cda5da8dSAndroid Build Coastguard Worker# | DD: Day 68*cda5da8dSAndroid Build Coastguard Worker# | Mon: Month (calendar name) 69*cda5da8dSAndroid Build Coastguard Worker# | YYYY: Year 70*cda5da8dSAndroid Build Coastguard Worker# | hh: hour (24-hour format, the machine's timezone) 71*cda5da8dSAndroid Build Coastguard Worker# | mm: minutes 72*cda5da8dSAndroid Build Coastguard Worker# | ss: seconds 73*cda5da8dSAndroid Build Coastguard Worker# | request: The first line of the HTTP request as sent by the client. 74*cda5da8dSAndroid Build Coastguard Worker# | ddd: the status code returned by the server, - if not available. 75*cda5da8dSAndroid Build Coastguard Worker# | bbbb: the total number of bytes sent, 76*cda5da8dSAndroid Build Coastguard Worker# | *not including the HTTP/1.0 header*, - if not available 77*cda5da8dSAndroid Build Coastguard Worker# | 78*cda5da8dSAndroid Build Coastguard Worker# | You can determine the name of the file accessed through request. 79*cda5da8dSAndroid Build Coastguard Worker# 80*cda5da8dSAndroid Build Coastguard Worker# (Actually, the latter is only true if you know the server configuration 81*cda5da8dSAndroid Build Coastguard Worker# at the time the request was made!) 82*cda5da8dSAndroid Build Coastguard Worker 83*cda5da8dSAndroid Build Coastguard Worker__version__ = "0.6" 84*cda5da8dSAndroid Build Coastguard Worker 85*cda5da8dSAndroid Build Coastguard Worker__all__ = [ 86*cda5da8dSAndroid Build Coastguard Worker "HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", 87*cda5da8dSAndroid Build Coastguard Worker "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler", 88*cda5da8dSAndroid Build Coastguard Worker] 89*cda5da8dSAndroid Build Coastguard Worker 90*cda5da8dSAndroid Build Coastguard Workerimport copy 91*cda5da8dSAndroid Build Coastguard Workerimport datetime 92*cda5da8dSAndroid Build Coastguard Workerimport email.utils 93*cda5da8dSAndroid Build Coastguard Workerimport html 94*cda5da8dSAndroid Build Coastguard Workerimport http.client 95*cda5da8dSAndroid Build Coastguard Workerimport io 96*cda5da8dSAndroid Build Coastguard Workerimport itertools 97*cda5da8dSAndroid Build Coastguard Workerimport mimetypes 98*cda5da8dSAndroid Build Coastguard Workerimport os 99*cda5da8dSAndroid Build Coastguard Workerimport posixpath 100*cda5da8dSAndroid Build Coastguard Workerimport select 101*cda5da8dSAndroid Build Coastguard Workerimport shutil 102*cda5da8dSAndroid Build Coastguard Workerimport socket # For gethostbyaddr() 103*cda5da8dSAndroid Build Coastguard Workerimport socketserver 104*cda5da8dSAndroid Build Coastguard Workerimport sys 105*cda5da8dSAndroid Build Coastguard Workerimport time 106*cda5da8dSAndroid Build Coastguard Workerimport urllib.parse 107*cda5da8dSAndroid Build Coastguard Worker 108*cda5da8dSAndroid Build Coastguard Workerfrom http import HTTPStatus 109*cda5da8dSAndroid Build Coastguard Worker 110*cda5da8dSAndroid Build Coastguard Worker 111*cda5da8dSAndroid Build Coastguard Worker# Default error message template 112*cda5da8dSAndroid Build Coastguard WorkerDEFAULT_ERROR_MESSAGE = """\ 113*cda5da8dSAndroid Build Coastguard Worker<!DOCTYPE HTML> 114*cda5da8dSAndroid Build Coastguard Worker<html lang="en"> 115*cda5da8dSAndroid Build Coastguard Worker <head> 116*cda5da8dSAndroid Build Coastguard Worker <meta charset="utf-8"> 117*cda5da8dSAndroid Build Coastguard Worker <title>Error response</title> 118*cda5da8dSAndroid Build Coastguard Worker </head> 119*cda5da8dSAndroid Build Coastguard Worker <body> 120*cda5da8dSAndroid Build Coastguard Worker <h1>Error response</h1> 121*cda5da8dSAndroid Build Coastguard Worker <p>Error code: %(code)d</p> 122*cda5da8dSAndroid Build Coastguard Worker <p>Message: %(message)s.</p> 123*cda5da8dSAndroid Build Coastguard Worker <p>Error code explanation: %(code)s - %(explain)s.</p> 124*cda5da8dSAndroid Build Coastguard Worker </body> 125*cda5da8dSAndroid Build Coastguard Worker</html> 126*cda5da8dSAndroid Build Coastguard Worker""" 127*cda5da8dSAndroid Build Coastguard Worker 128*cda5da8dSAndroid Build Coastguard WorkerDEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8" 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Workerclass HTTPServer(socketserver.TCPServer): 131*cda5da8dSAndroid Build Coastguard Worker 132*cda5da8dSAndroid Build Coastguard Worker allow_reuse_address = 1 # Seems to make sense in testing environment 133*cda5da8dSAndroid Build Coastguard Worker 134*cda5da8dSAndroid Build Coastguard Worker def server_bind(self): 135*cda5da8dSAndroid Build Coastguard Worker """Override server_bind to store the server name.""" 136*cda5da8dSAndroid Build Coastguard Worker socketserver.TCPServer.server_bind(self) 137*cda5da8dSAndroid Build Coastguard Worker host, port = self.server_address[:2] 138*cda5da8dSAndroid Build Coastguard Worker self.server_name = socket.getfqdn(host) 139*cda5da8dSAndroid Build Coastguard Worker self.server_port = port 140*cda5da8dSAndroid Build Coastguard Worker 141*cda5da8dSAndroid Build Coastguard Worker 142*cda5da8dSAndroid Build Coastguard Workerclass ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): 143*cda5da8dSAndroid Build Coastguard Worker daemon_threads = True 144*cda5da8dSAndroid Build Coastguard Worker 145*cda5da8dSAndroid Build Coastguard Worker 146*cda5da8dSAndroid Build Coastguard Workerclass BaseHTTPRequestHandler(socketserver.StreamRequestHandler): 147*cda5da8dSAndroid Build Coastguard Worker 148*cda5da8dSAndroid Build Coastguard Worker """HTTP request handler base class. 149*cda5da8dSAndroid Build Coastguard Worker 150*cda5da8dSAndroid Build Coastguard Worker The following explanation of HTTP serves to guide you through the 151*cda5da8dSAndroid Build Coastguard Worker code as well as to expose any misunderstandings I may have about 152*cda5da8dSAndroid Build Coastguard Worker HTTP (so you don't need to read the code to figure out I'm wrong 153*cda5da8dSAndroid Build Coastguard Worker :-). 154*cda5da8dSAndroid Build Coastguard Worker 155*cda5da8dSAndroid Build Coastguard Worker HTTP (HyperText Transfer Protocol) is an extensible protocol on 156*cda5da8dSAndroid Build Coastguard Worker top of a reliable stream transport (e.g. TCP/IP). The protocol 157*cda5da8dSAndroid Build Coastguard Worker recognizes three parts to a request: 158*cda5da8dSAndroid Build Coastguard Worker 159*cda5da8dSAndroid Build Coastguard Worker 1. One line identifying the request type and path 160*cda5da8dSAndroid Build Coastguard Worker 2. An optional set of RFC-822-style headers 161*cda5da8dSAndroid Build Coastguard Worker 3. An optional data part 162*cda5da8dSAndroid Build Coastguard Worker 163*cda5da8dSAndroid Build Coastguard Worker The headers and data are separated by a blank line. 164*cda5da8dSAndroid Build Coastguard Worker 165*cda5da8dSAndroid Build Coastguard Worker The first line of the request has the form 166*cda5da8dSAndroid Build Coastguard Worker 167*cda5da8dSAndroid Build Coastguard Worker <command> <path> <version> 168*cda5da8dSAndroid Build Coastguard Worker 169*cda5da8dSAndroid Build Coastguard Worker where <command> is a (case-sensitive) keyword such as GET or POST, 170*cda5da8dSAndroid Build Coastguard Worker <path> is a string containing path information for the request, 171*cda5da8dSAndroid Build Coastguard Worker and <version> should be the string "HTTP/1.0" or "HTTP/1.1". 172*cda5da8dSAndroid Build Coastguard Worker <path> is encoded using the URL encoding scheme (using %xx to signify 173*cda5da8dSAndroid Build Coastguard Worker the ASCII character with hex code xx). 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker The specification specifies that lines are separated by CRLF but 176*cda5da8dSAndroid Build Coastguard Worker for compatibility with the widest range of clients recommends 177*cda5da8dSAndroid Build Coastguard Worker servers also handle LF. Similarly, whitespace in the request line 178*cda5da8dSAndroid Build Coastguard Worker is treated sensibly (allowing multiple spaces between components 179*cda5da8dSAndroid Build Coastguard Worker and allowing trailing whitespace). 180*cda5da8dSAndroid Build Coastguard Worker 181*cda5da8dSAndroid Build Coastguard Worker Similarly, for output, lines ought to be separated by CRLF pairs 182*cda5da8dSAndroid Build Coastguard Worker but most clients grok LF characters just fine. 183*cda5da8dSAndroid Build Coastguard Worker 184*cda5da8dSAndroid Build Coastguard Worker If the first line of the request has the form 185*cda5da8dSAndroid Build Coastguard Worker 186*cda5da8dSAndroid Build Coastguard Worker <command> <path> 187*cda5da8dSAndroid Build Coastguard Worker 188*cda5da8dSAndroid Build Coastguard Worker (i.e. <version> is left out) then this is assumed to be an HTTP 189*cda5da8dSAndroid Build Coastguard Worker 0.9 request; this form has no optional headers and data part and 190*cda5da8dSAndroid Build Coastguard Worker the reply consists of just the data. 191*cda5da8dSAndroid Build Coastguard Worker 192*cda5da8dSAndroid Build Coastguard Worker The reply form of the HTTP 1.x protocol again has three parts: 193*cda5da8dSAndroid Build Coastguard Worker 194*cda5da8dSAndroid Build Coastguard Worker 1. One line giving the response code 195*cda5da8dSAndroid Build Coastguard Worker 2. An optional set of RFC-822-style headers 196*cda5da8dSAndroid Build Coastguard Worker 3. The data 197*cda5da8dSAndroid Build Coastguard Worker 198*cda5da8dSAndroid Build Coastguard Worker Again, the headers and data are separated by a blank line. 199*cda5da8dSAndroid Build Coastguard Worker 200*cda5da8dSAndroid Build Coastguard Worker The response code line has the form 201*cda5da8dSAndroid Build Coastguard Worker 202*cda5da8dSAndroid Build Coastguard Worker <version> <responsecode> <responsestring> 203*cda5da8dSAndroid Build Coastguard Worker 204*cda5da8dSAndroid Build Coastguard Worker where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"), 205*cda5da8dSAndroid Build Coastguard Worker <responsecode> is a 3-digit response code indicating success or 206*cda5da8dSAndroid Build Coastguard Worker failure of the request, and <responsestring> is an optional 207*cda5da8dSAndroid Build Coastguard Worker human-readable string explaining what the response code means. 208*cda5da8dSAndroid Build Coastguard Worker 209*cda5da8dSAndroid Build Coastguard Worker This server parses the request and the headers, and then calls a 210*cda5da8dSAndroid Build Coastguard Worker function specific to the request type (<command>). Specifically, 211*cda5da8dSAndroid Build Coastguard Worker a request SPAM will be handled by a method do_SPAM(). If no 212*cda5da8dSAndroid Build Coastguard Worker such method exists the server sends an error response to the 213*cda5da8dSAndroid Build Coastguard Worker client. If it exists, it is called with no arguments: 214*cda5da8dSAndroid Build Coastguard Worker 215*cda5da8dSAndroid Build Coastguard Worker do_SPAM() 216*cda5da8dSAndroid Build Coastguard Worker 217*cda5da8dSAndroid Build Coastguard Worker Note that the request name is case sensitive (i.e. SPAM and spam 218*cda5da8dSAndroid Build Coastguard Worker are different requests). 219*cda5da8dSAndroid Build Coastguard Worker 220*cda5da8dSAndroid Build Coastguard Worker The various request details are stored in instance variables: 221*cda5da8dSAndroid Build Coastguard Worker 222*cda5da8dSAndroid Build Coastguard Worker - client_address is the client IP address in the form (host, 223*cda5da8dSAndroid Build Coastguard Worker port); 224*cda5da8dSAndroid Build Coastguard Worker 225*cda5da8dSAndroid Build Coastguard Worker - command, path and version are the broken-down request line; 226*cda5da8dSAndroid Build Coastguard Worker 227*cda5da8dSAndroid Build Coastguard Worker - headers is an instance of email.message.Message (or a derived 228*cda5da8dSAndroid Build Coastguard Worker class) containing the header information; 229*cda5da8dSAndroid Build Coastguard Worker 230*cda5da8dSAndroid Build Coastguard Worker - rfile is a file object open for reading positioned at the 231*cda5da8dSAndroid Build Coastguard Worker start of the optional input data part; 232*cda5da8dSAndroid Build Coastguard Worker 233*cda5da8dSAndroid Build Coastguard Worker - wfile is a file object open for writing. 234*cda5da8dSAndroid Build Coastguard Worker 235*cda5da8dSAndroid Build Coastguard Worker IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING! 236*cda5da8dSAndroid Build Coastguard Worker 237*cda5da8dSAndroid Build Coastguard Worker The first thing to be written must be the response line. Then 238*cda5da8dSAndroid Build Coastguard Worker follow 0 or more header lines, then a blank line, and then the 239*cda5da8dSAndroid Build Coastguard Worker actual data (if any). The meaning of the header lines depends on 240*cda5da8dSAndroid Build Coastguard Worker the command executed by the server; in most cases, when data is 241*cda5da8dSAndroid Build Coastguard Worker returned, there should be at least one header line of the form 242*cda5da8dSAndroid Build Coastguard Worker 243*cda5da8dSAndroid Build Coastguard Worker Content-type: <type>/<subtype> 244*cda5da8dSAndroid Build Coastguard Worker 245*cda5da8dSAndroid Build Coastguard Worker where <type> and <subtype> should be registered MIME types, 246*cda5da8dSAndroid Build Coastguard Worker e.g. "text/html" or "text/plain". 247*cda5da8dSAndroid Build Coastguard Worker 248*cda5da8dSAndroid Build Coastguard Worker """ 249*cda5da8dSAndroid Build Coastguard Worker 250*cda5da8dSAndroid Build Coastguard Worker # The Python system version, truncated to its first component. 251*cda5da8dSAndroid Build Coastguard Worker sys_version = "Python/" + sys.version.split()[0] 252*cda5da8dSAndroid Build Coastguard Worker 253*cda5da8dSAndroid Build Coastguard Worker # The server software version. You may want to override this. 254*cda5da8dSAndroid Build Coastguard Worker # The format is multiple whitespace-separated strings, 255*cda5da8dSAndroid Build Coastguard Worker # where each string is of the form name[/version]. 256*cda5da8dSAndroid Build Coastguard Worker server_version = "BaseHTTP/" + __version__ 257*cda5da8dSAndroid Build Coastguard Worker 258*cda5da8dSAndroid Build Coastguard Worker error_message_format = DEFAULT_ERROR_MESSAGE 259*cda5da8dSAndroid Build Coastguard Worker error_content_type = DEFAULT_ERROR_CONTENT_TYPE 260*cda5da8dSAndroid Build Coastguard Worker 261*cda5da8dSAndroid Build Coastguard Worker # The default request version. This only affects responses up until 262*cda5da8dSAndroid Build Coastguard Worker # the point where the request line is parsed, so it mainly decides what 263*cda5da8dSAndroid Build Coastguard Worker # the client gets back when sending a malformed request line. 264*cda5da8dSAndroid Build Coastguard Worker # Most web servers default to HTTP 0.9, i.e. don't send a status line. 265*cda5da8dSAndroid Build Coastguard Worker default_request_version = "HTTP/0.9" 266*cda5da8dSAndroid Build Coastguard Worker 267*cda5da8dSAndroid Build Coastguard Worker def parse_request(self): 268*cda5da8dSAndroid Build Coastguard Worker """Parse a request (internal). 269*cda5da8dSAndroid Build Coastguard Worker 270*cda5da8dSAndroid Build Coastguard Worker The request should be stored in self.raw_requestline; the results 271*cda5da8dSAndroid Build Coastguard Worker are in self.command, self.path, self.request_version and 272*cda5da8dSAndroid Build Coastguard Worker self.headers. 273*cda5da8dSAndroid Build Coastguard Worker 274*cda5da8dSAndroid Build Coastguard Worker Return True for success, False for failure; on failure, any relevant 275*cda5da8dSAndroid Build Coastguard Worker error response has already been sent back. 276*cda5da8dSAndroid Build Coastguard Worker 277*cda5da8dSAndroid Build Coastguard Worker """ 278*cda5da8dSAndroid Build Coastguard Worker self.command = None # set in case of error on the first line 279*cda5da8dSAndroid Build Coastguard Worker self.request_version = version = self.default_request_version 280*cda5da8dSAndroid Build Coastguard Worker self.close_connection = True 281*cda5da8dSAndroid Build Coastguard Worker requestline = str(self.raw_requestline, 'iso-8859-1') 282*cda5da8dSAndroid Build Coastguard Worker requestline = requestline.rstrip('\r\n') 283*cda5da8dSAndroid Build Coastguard Worker self.requestline = requestline 284*cda5da8dSAndroid Build Coastguard Worker words = requestline.split() 285*cda5da8dSAndroid Build Coastguard Worker if len(words) == 0: 286*cda5da8dSAndroid Build Coastguard Worker return False 287*cda5da8dSAndroid Build Coastguard Worker 288*cda5da8dSAndroid Build Coastguard Worker if len(words) >= 3: # Enough to determine protocol version 289*cda5da8dSAndroid Build Coastguard Worker version = words[-1] 290*cda5da8dSAndroid Build Coastguard Worker try: 291*cda5da8dSAndroid Build Coastguard Worker if not version.startswith('HTTP/'): 292*cda5da8dSAndroid Build Coastguard Worker raise ValueError 293*cda5da8dSAndroid Build Coastguard Worker base_version_number = version.split('/', 1)[1] 294*cda5da8dSAndroid Build Coastguard Worker version_number = base_version_number.split(".") 295*cda5da8dSAndroid Build Coastguard Worker # RFC 2145 section 3.1 says there can be only one "." and 296*cda5da8dSAndroid Build Coastguard Worker # - major and minor numbers MUST be treated as 297*cda5da8dSAndroid Build Coastguard Worker # separate integers; 298*cda5da8dSAndroid Build Coastguard Worker # - HTTP/2.4 is a lower version than HTTP/2.13, which in 299*cda5da8dSAndroid Build Coastguard Worker # turn is lower than HTTP/12.3; 300*cda5da8dSAndroid Build Coastguard Worker # - Leading zeros MUST be ignored by recipients. 301*cda5da8dSAndroid Build Coastguard Worker if len(version_number) != 2: 302*cda5da8dSAndroid Build Coastguard Worker raise ValueError 303*cda5da8dSAndroid Build Coastguard Worker if any(not component.isdigit() for component in version_number): 304*cda5da8dSAndroid Build Coastguard Worker raise ValueError("non digit in http version") 305*cda5da8dSAndroid Build Coastguard Worker if any(len(component) > 10 for component in version_number): 306*cda5da8dSAndroid Build Coastguard Worker raise ValueError("unreasonable length http version") 307*cda5da8dSAndroid Build Coastguard Worker version_number = int(version_number[0]), int(version_number[1]) 308*cda5da8dSAndroid Build Coastguard Worker except (ValueError, IndexError): 309*cda5da8dSAndroid Build Coastguard Worker self.send_error( 310*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.BAD_REQUEST, 311*cda5da8dSAndroid Build Coastguard Worker "Bad request version (%r)" % version) 312*cda5da8dSAndroid Build Coastguard Worker return False 313*cda5da8dSAndroid Build Coastguard Worker if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": 314*cda5da8dSAndroid Build Coastguard Worker self.close_connection = False 315*cda5da8dSAndroid Build Coastguard Worker if version_number >= (2, 0): 316*cda5da8dSAndroid Build Coastguard Worker self.send_error( 317*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.HTTP_VERSION_NOT_SUPPORTED, 318*cda5da8dSAndroid Build Coastguard Worker "Invalid HTTP version (%s)" % base_version_number) 319*cda5da8dSAndroid Build Coastguard Worker return False 320*cda5da8dSAndroid Build Coastguard Worker self.request_version = version 321*cda5da8dSAndroid Build Coastguard Worker 322*cda5da8dSAndroid Build Coastguard Worker if not 2 <= len(words) <= 3: 323*cda5da8dSAndroid Build Coastguard Worker self.send_error( 324*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.BAD_REQUEST, 325*cda5da8dSAndroid Build Coastguard Worker "Bad request syntax (%r)" % requestline) 326*cda5da8dSAndroid Build Coastguard Worker return False 327*cda5da8dSAndroid Build Coastguard Worker command, path = words[:2] 328*cda5da8dSAndroid Build Coastguard Worker if len(words) == 2: 329*cda5da8dSAndroid Build Coastguard Worker self.close_connection = True 330*cda5da8dSAndroid Build Coastguard Worker if command != 'GET': 331*cda5da8dSAndroid Build Coastguard Worker self.send_error( 332*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.BAD_REQUEST, 333*cda5da8dSAndroid Build Coastguard Worker "Bad HTTP/0.9 request type (%r)" % command) 334*cda5da8dSAndroid Build Coastguard Worker return False 335*cda5da8dSAndroid Build Coastguard Worker self.command, self.path = command, path 336*cda5da8dSAndroid Build Coastguard Worker 337*cda5da8dSAndroid Build Coastguard Worker # gh-87389: The purpose of replacing '//' with '/' is to protect 338*cda5da8dSAndroid Build Coastguard Worker # against open redirect attacks possibly triggered if the path starts 339*cda5da8dSAndroid Build Coastguard Worker # with '//' because http clients treat //path as an absolute URI 340*cda5da8dSAndroid Build Coastguard Worker # without scheme (similar to http://path) rather than a path. 341*cda5da8dSAndroid Build Coastguard Worker if self.path.startswith('//'): 342*cda5da8dSAndroid Build Coastguard Worker self.path = '/' + self.path.lstrip('/') # Reduce to a single / 343*cda5da8dSAndroid Build Coastguard Worker 344*cda5da8dSAndroid Build Coastguard Worker # Examine the headers and look for a Connection directive. 345*cda5da8dSAndroid Build Coastguard Worker try: 346*cda5da8dSAndroid Build Coastguard Worker self.headers = http.client.parse_headers(self.rfile, 347*cda5da8dSAndroid Build Coastguard Worker _class=self.MessageClass) 348*cda5da8dSAndroid Build Coastguard Worker except http.client.LineTooLong as err: 349*cda5da8dSAndroid Build Coastguard Worker self.send_error( 350*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE, 351*cda5da8dSAndroid Build Coastguard Worker "Line too long", 352*cda5da8dSAndroid Build Coastguard Worker str(err)) 353*cda5da8dSAndroid Build Coastguard Worker return False 354*cda5da8dSAndroid Build Coastguard Worker except http.client.HTTPException as err: 355*cda5da8dSAndroid Build Coastguard Worker self.send_error( 356*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE, 357*cda5da8dSAndroid Build Coastguard Worker "Too many headers", 358*cda5da8dSAndroid Build Coastguard Worker str(err) 359*cda5da8dSAndroid Build Coastguard Worker ) 360*cda5da8dSAndroid Build Coastguard Worker return False 361*cda5da8dSAndroid Build Coastguard Worker 362*cda5da8dSAndroid Build Coastguard Worker conntype = self.headers.get('Connection', "") 363*cda5da8dSAndroid Build Coastguard Worker if conntype.lower() == 'close': 364*cda5da8dSAndroid Build Coastguard Worker self.close_connection = True 365*cda5da8dSAndroid Build Coastguard Worker elif (conntype.lower() == 'keep-alive' and 366*cda5da8dSAndroid Build Coastguard Worker self.protocol_version >= "HTTP/1.1"): 367*cda5da8dSAndroid Build Coastguard Worker self.close_connection = False 368*cda5da8dSAndroid Build Coastguard Worker # Examine the headers and look for an Expect directive 369*cda5da8dSAndroid Build Coastguard Worker expect = self.headers.get('Expect', "") 370*cda5da8dSAndroid Build Coastguard Worker if (expect.lower() == "100-continue" and 371*cda5da8dSAndroid Build Coastguard Worker self.protocol_version >= "HTTP/1.1" and 372*cda5da8dSAndroid Build Coastguard Worker self.request_version >= "HTTP/1.1"): 373*cda5da8dSAndroid Build Coastguard Worker if not self.handle_expect_100(): 374*cda5da8dSAndroid Build Coastguard Worker return False 375*cda5da8dSAndroid Build Coastguard Worker return True 376*cda5da8dSAndroid Build Coastguard Worker 377*cda5da8dSAndroid Build Coastguard Worker def handle_expect_100(self): 378*cda5da8dSAndroid Build Coastguard Worker """Decide what to do with an "Expect: 100-continue" header. 379*cda5da8dSAndroid Build Coastguard Worker 380*cda5da8dSAndroid Build Coastguard Worker If the client is expecting a 100 Continue response, we must 381*cda5da8dSAndroid Build Coastguard Worker respond with either a 100 Continue or a final response before 382*cda5da8dSAndroid Build Coastguard Worker waiting for the request body. The default is to always respond 383*cda5da8dSAndroid Build Coastguard Worker with a 100 Continue. You can behave differently (for example, 384*cda5da8dSAndroid Build Coastguard Worker reject unauthorized requests) by overriding this method. 385*cda5da8dSAndroid Build Coastguard Worker 386*cda5da8dSAndroid Build Coastguard Worker This method should either return True (possibly after sending 387*cda5da8dSAndroid Build Coastguard Worker a 100 Continue response) or send an error response and return 388*cda5da8dSAndroid Build Coastguard Worker False. 389*cda5da8dSAndroid Build Coastguard Worker 390*cda5da8dSAndroid Build Coastguard Worker """ 391*cda5da8dSAndroid Build Coastguard Worker self.send_response_only(HTTPStatus.CONTINUE) 392*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 393*cda5da8dSAndroid Build Coastguard Worker return True 394*cda5da8dSAndroid Build Coastguard Worker 395*cda5da8dSAndroid Build Coastguard Worker def handle_one_request(self): 396*cda5da8dSAndroid Build Coastguard Worker """Handle a single HTTP request. 397*cda5da8dSAndroid Build Coastguard Worker 398*cda5da8dSAndroid Build Coastguard Worker You normally don't need to override this method; see the class 399*cda5da8dSAndroid Build Coastguard Worker __doc__ string for information on how to handle specific HTTP 400*cda5da8dSAndroid Build Coastguard Worker commands such as GET and POST. 401*cda5da8dSAndroid Build Coastguard Worker 402*cda5da8dSAndroid Build Coastguard Worker """ 403*cda5da8dSAndroid Build Coastguard Worker try: 404*cda5da8dSAndroid Build Coastguard Worker self.raw_requestline = self.rfile.readline(65537) 405*cda5da8dSAndroid Build Coastguard Worker if len(self.raw_requestline) > 65536: 406*cda5da8dSAndroid Build Coastguard Worker self.requestline = '' 407*cda5da8dSAndroid Build Coastguard Worker self.request_version = '' 408*cda5da8dSAndroid Build Coastguard Worker self.command = '' 409*cda5da8dSAndroid Build Coastguard Worker self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG) 410*cda5da8dSAndroid Build Coastguard Worker return 411*cda5da8dSAndroid Build Coastguard Worker if not self.raw_requestline: 412*cda5da8dSAndroid Build Coastguard Worker self.close_connection = True 413*cda5da8dSAndroid Build Coastguard Worker return 414*cda5da8dSAndroid Build Coastguard Worker if not self.parse_request(): 415*cda5da8dSAndroid Build Coastguard Worker # An error code has been sent, just exit 416*cda5da8dSAndroid Build Coastguard Worker return 417*cda5da8dSAndroid Build Coastguard Worker mname = 'do_' + self.command 418*cda5da8dSAndroid Build Coastguard Worker if not hasattr(self, mname): 419*cda5da8dSAndroid Build Coastguard Worker self.send_error( 420*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.NOT_IMPLEMENTED, 421*cda5da8dSAndroid Build Coastguard Worker "Unsupported method (%r)" % self.command) 422*cda5da8dSAndroid Build Coastguard Worker return 423*cda5da8dSAndroid Build Coastguard Worker method = getattr(self, mname) 424*cda5da8dSAndroid Build Coastguard Worker method() 425*cda5da8dSAndroid Build Coastguard Worker self.wfile.flush() #actually send the response if not already done. 426*cda5da8dSAndroid Build Coastguard Worker except TimeoutError as e: 427*cda5da8dSAndroid Build Coastguard Worker #a read or a write timed out. Discard this connection 428*cda5da8dSAndroid Build Coastguard Worker self.log_error("Request timed out: %r", e) 429*cda5da8dSAndroid Build Coastguard Worker self.close_connection = True 430*cda5da8dSAndroid Build Coastguard Worker return 431*cda5da8dSAndroid Build Coastguard Worker 432*cda5da8dSAndroid Build Coastguard Worker def handle(self): 433*cda5da8dSAndroid Build Coastguard Worker """Handle multiple requests if necessary.""" 434*cda5da8dSAndroid Build Coastguard Worker self.close_connection = True 435*cda5da8dSAndroid Build Coastguard Worker 436*cda5da8dSAndroid Build Coastguard Worker self.handle_one_request() 437*cda5da8dSAndroid Build Coastguard Worker while not self.close_connection: 438*cda5da8dSAndroid Build Coastguard Worker self.handle_one_request() 439*cda5da8dSAndroid Build Coastguard Worker 440*cda5da8dSAndroid Build Coastguard Worker def send_error(self, code, message=None, explain=None): 441*cda5da8dSAndroid Build Coastguard Worker """Send and log an error reply. 442*cda5da8dSAndroid Build Coastguard Worker 443*cda5da8dSAndroid Build Coastguard Worker Arguments are 444*cda5da8dSAndroid Build Coastguard Worker * code: an HTTP error code 445*cda5da8dSAndroid Build Coastguard Worker 3 digits 446*cda5da8dSAndroid Build Coastguard Worker * message: a simple optional 1 line reason phrase. 447*cda5da8dSAndroid Build Coastguard Worker *( HTAB / SP / VCHAR / %x80-FF ) 448*cda5da8dSAndroid Build Coastguard Worker defaults to short entry matching the response code 449*cda5da8dSAndroid Build Coastguard Worker * explain: a detailed message defaults to the long entry 450*cda5da8dSAndroid Build Coastguard Worker matching the response code. 451*cda5da8dSAndroid Build Coastguard Worker 452*cda5da8dSAndroid Build Coastguard Worker This sends an error response (so it must be called before any 453*cda5da8dSAndroid Build Coastguard Worker output has been generated), logs the error, and finally sends 454*cda5da8dSAndroid Build Coastguard Worker a piece of HTML explaining the error to the user. 455*cda5da8dSAndroid Build Coastguard Worker 456*cda5da8dSAndroid Build Coastguard Worker """ 457*cda5da8dSAndroid Build Coastguard Worker 458*cda5da8dSAndroid Build Coastguard Worker try: 459*cda5da8dSAndroid Build Coastguard Worker shortmsg, longmsg = self.responses[code] 460*cda5da8dSAndroid Build Coastguard Worker except KeyError: 461*cda5da8dSAndroid Build Coastguard Worker shortmsg, longmsg = '???', '???' 462*cda5da8dSAndroid Build Coastguard Worker if message is None: 463*cda5da8dSAndroid Build Coastguard Worker message = shortmsg 464*cda5da8dSAndroid Build Coastguard Worker if explain is None: 465*cda5da8dSAndroid Build Coastguard Worker explain = longmsg 466*cda5da8dSAndroid Build Coastguard Worker self.log_error("code %d, message %s", code, message) 467*cda5da8dSAndroid Build Coastguard Worker self.send_response(code, message) 468*cda5da8dSAndroid Build Coastguard Worker self.send_header('Connection', 'close') 469*cda5da8dSAndroid Build Coastguard Worker 470*cda5da8dSAndroid Build Coastguard Worker # Message body is omitted for cases described in: 471*cda5da8dSAndroid Build Coastguard Worker # - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified) 472*cda5da8dSAndroid Build Coastguard Worker # - RFC7231: 6.3.6. 205(Reset Content) 473*cda5da8dSAndroid Build Coastguard Worker body = None 474*cda5da8dSAndroid Build Coastguard Worker if (code >= 200 and 475*cda5da8dSAndroid Build Coastguard Worker code not in (HTTPStatus.NO_CONTENT, 476*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.RESET_CONTENT, 477*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.NOT_MODIFIED)): 478*cda5da8dSAndroid Build Coastguard Worker # HTML encode to prevent Cross Site Scripting attacks 479*cda5da8dSAndroid Build Coastguard Worker # (see bug #1100201) 480*cda5da8dSAndroid Build Coastguard Worker content = (self.error_message_format % { 481*cda5da8dSAndroid Build Coastguard Worker 'code': code, 482*cda5da8dSAndroid Build Coastguard Worker 'message': html.escape(message, quote=False), 483*cda5da8dSAndroid Build Coastguard Worker 'explain': html.escape(explain, quote=False) 484*cda5da8dSAndroid Build Coastguard Worker }) 485*cda5da8dSAndroid Build Coastguard Worker body = content.encode('UTF-8', 'replace') 486*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-Type", self.error_content_type) 487*cda5da8dSAndroid Build Coastguard Worker self.send_header('Content-Length', str(len(body))) 488*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 489*cda5da8dSAndroid Build Coastguard Worker 490*cda5da8dSAndroid Build Coastguard Worker if self.command != 'HEAD' and body: 491*cda5da8dSAndroid Build Coastguard Worker self.wfile.write(body) 492*cda5da8dSAndroid Build Coastguard Worker 493*cda5da8dSAndroid Build Coastguard Worker def send_response(self, code, message=None): 494*cda5da8dSAndroid Build Coastguard Worker """Add the response header to the headers buffer and log the 495*cda5da8dSAndroid Build Coastguard Worker response code. 496*cda5da8dSAndroid Build Coastguard Worker 497*cda5da8dSAndroid Build Coastguard Worker Also send two standard headers with the server software 498*cda5da8dSAndroid Build Coastguard Worker version and the current date. 499*cda5da8dSAndroid Build Coastguard Worker 500*cda5da8dSAndroid Build Coastguard Worker """ 501*cda5da8dSAndroid Build Coastguard Worker self.log_request(code) 502*cda5da8dSAndroid Build Coastguard Worker self.send_response_only(code, message) 503*cda5da8dSAndroid Build Coastguard Worker self.send_header('Server', self.version_string()) 504*cda5da8dSAndroid Build Coastguard Worker self.send_header('Date', self.date_time_string()) 505*cda5da8dSAndroid Build Coastguard Worker 506*cda5da8dSAndroid Build Coastguard Worker def send_response_only(self, code, message=None): 507*cda5da8dSAndroid Build Coastguard Worker """Send the response header only.""" 508*cda5da8dSAndroid Build Coastguard Worker if self.request_version != 'HTTP/0.9': 509*cda5da8dSAndroid Build Coastguard Worker if message is None: 510*cda5da8dSAndroid Build Coastguard Worker if code in self.responses: 511*cda5da8dSAndroid Build Coastguard Worker message = self.responses[code][0] 512*cda5da8dSAndroid Build Coastguard Worker else: 513*cda5da8dSAndroid Build Coastguard Worker message = '' 514*cda5da8dSAndroid Build Coastguard Worker if not hasattr(self, '_headers_buffer'): 515*cda5da8dSAndroid Build Coastguard Worker self._headers_buffer = [] 516*cda5da8dSAndroid Build Coastguard Worker self._headers_buffer.append(("%s %d %s\r\n" % 517*cda5da8dSAndroid Build Coastguard Worker (self.protocol_version, code, message)).encode( 518*cda5da8dSAndroid Build Coastguard Worker 'latin-1', 'strict')) 519*cda5da8dSAndroid Build Coastguard Worker 520*cda5da8dSAndroid Build Coastguard Worker def send_header(self, keyword, value): 521*cda5da8dSAndroid Build Coastguard Worker """Send a MIME header to the headers buffer.""" 522*cda5da8dSAndroid Build Coastguard Worker if self.request_version != 'HTTP/0.9': 523*cda5da8dSAndroid Build Coastguard Worker if not hasattr(self, '_headers_buffer'): 524*cda5da8dSAndroid Build Coastguard Worker self._headers_buffer = [] 525*cda5da8dSAndroid Build Coastguard Worker self._headers_buffer.append( 526*cda5da8dSAndroid Build Coastguard Worker ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) 527*cda5da8dSAndroid Build Coastguard Worker 528*cda5da8dSAndroid Build Coastguard Worker if keyword.lower() == 'connection': 529*cda5da8dSAndroid Build Coastguard Worker if value.lower() == 'close': 530*cda5da8dSAndroid Build Coastguard Worker self.close_connection = True 531*cda5da8dSAndroid Build Coastguard Worker elif value.lower() == 'keep-alive': 532*cda5da8dSAndroid Build Coastguard Worker self.close_connection = False 533*cda5da8dSAndroid Build Coastguard Worker 534*cda5da8dSAndroid Build Coastguard Worker def end_headers(self): 535*cda5da8dSAndroid Build Coastguard Worker """Send the blank line ending the MIME headers.""" 536*cda5da8dSAndroid Build Coastguard Worker if self.request_version != 'HTTP/0.9': 537*cda5da8dSAndroid Build Coastguard Worker self._headers_buffer.append(b"\r\n") 538*cda5da8dSAndroid Build Coastguard Worker self.flush_headers() 539*cda5da8dSAndroid Build Coastguard Worker 540*cda5da8dSAndroid Build Coastguard Worker def flush_headers(self): 541*cda5da8dSAndroid Build Coastguard Worker if hasattr(self, '_headers_buffer'): 542*cda5da8dSAndroid Build Coastguard Worker self.wfile.write(b"".join(self._headers_buffer)) 543*cda5da8dSAndroid Build Coastguard Worker self._headers_buffer = [] 544*cda5da8dSAndroid Build Coastguard Worker 545*cda5da8dSAndroid Build Coastguard Worker def log_request(self, code='-', size='-'): 546*cda5da8dSAndroid Build Coastguard Worker """Log an accepted request. 547*cda5da8dSAndroid Build Coastguard Worker 548*cda5da8dSAndroid Build Coastguard Worker This is called by send_response(). 549*cda5da8dSAndroid Build Coastguard Worker 550*cda5da8dSAndroid Build Coastguard Worker """ 551*cda5da8dSAndroid Build Coastguard Worker if isinstance(code, HTTPStatus): 552*cda5da8dSAndroid Build Coastguard Worker code = code.value 553*cda5da8dSAndroid Build Coastguard Worker self.log_message('"%s" %s %s', 554*cda5da8dSAndroid Build Coastguard Worker self.requestline, str(code), str(size)) 555*cda5da8dSAndroid Build Coastguard Worker 556*cda5da8dSAndroid Build Coastguard Worker def log_error(self, format, *args): 557*cda5da8dSAndroid Build Coastguard Worker """Log an error. 558*cda5da8dSAndroid Build Coastguard Worker 559*cda5da8dSAndroid Build Coastguard Worker This is called when a request cannot be fulfilled. By 560*cda5da8dSAndroid Build Coastguard Worker default it passes the message on to log_message(). 561*cda5da8dSAndroid Build Coastguard Worker 562*cda5da8dSAndroid Build Coastguard Worker Arguments are the same as for log_message(). 563*cda5da8dSAndroid Build Coastguard Worker 564*cda5da8dSAndroid Build Coastguard Worker XXX This should go to the separate error log. 565*cda5da8dSAndroid Build Coastguard Worker 566*cda5da8dSAndroid Build Coastguard Worker """ 567*cda5da8dSAndroid Build Coastguard Worker 568*cda5da8dSAndroid Build Coastguard Worker self.log_message(format, *args) 569*cda5da8dSAndroid Build Coastguard Worker 570*cda5da8dSAndroid Build Coastguard Worker # https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes 571*cda5da8dSAndroid Build Coastguard Worker _control_char_table = str.maketrans( 572*cda5da8dSAndroid Build Coastguard Worker {c: fr'\x{c:02x}' for c in itertools.chain(range(0x20), range(0x7f,0xa0))}) 573*cda5da8dSAndroid Build Coastguard Worker _control_char_table[ord('\\')] = r'\\' 574*cda5da8dSAndroid Build Coastguard Worker 575*cda5da8dSAndroid Build Coastguard Worker def log_message(self, format, *args): 576*cda5da8dSAndroid Build Coastguard Worker """Log an arbitrary message. 577*cda5da8dSAndroid Build Coastguard Worker 578*cda5da8dSAndroid Build Coastguard Worker This is used by all other logging functions. Override 579*cda5da8dSAndroid Build Coastguard Worker it if you have specific logging wishes. 580*cda5da8dSAndroid Build Coastguard Worker 581*cda5da8dSAndroid Build Coastguard Worker The first argument, FORMAT, is a format string for the 582*cda5da8dSAndroid Build Coastguard Worker message to be logged. If the format string contains 583*cda5da8dSAndroid Build Coastguard Worker any % escapes requiring parameters, they should be 584*cda5da8dSAndroid Build Coastguard Worker specified as subsequent arguments (it's just like 585*cda5da8dSAndroid Build Coastguard Worker printf!). 586*cda5da8dSAndroid Build Coastguard Worker 587*cda5da8dSAndroid Build Coastguard Worker The client ip and current date/time are prefixed to 588*cda5da8dSAndroid Build Coastguard Worker every message. 589*cda5da8dSAndroid Build Coastguard Worker 590*cda5da8dSAndroid Build Coastguard Worker Unicode control characters are replaced with escaped hex 591*cda5da8dSAndroid Build Coastguard Worker before writing the output to stderr. 592*cda5da8dSAndroid Build Coastguard Worker 593*cda5da8dSAndroid Build Coastguard Worker """ 594*cda5da8dSAndroid Build Coastguard Worker 595*cda5da8dSAndroid Build Coastguard Worker message = format % args 596*cda5da8dSAndroid Build Coastguard Worker sys.stderr.write("%s - - [%s] %s\n" % 597*cda5da8dSAndroid Build Coastguard Worker (self.address_string(), 598*cda5da8dSAndroid Build Coastguard Worker self.log_date_time_string(), 599*cda5da8dSAndroid Build Coastguard Worker message.translate(self._control_char_table))) 600*cda5da8dSAndroid Build Coastguard Worker 601*cda5da8dSAndroid Build Coastguard Worker def version_string(self): 602*cda5da8dSAndroid Build Coastguard Worker """Return the server software version string.""" 603*cda5da8dSAndroid Build Coastguard Worker return self.server_version + ' ' + self.sys_version 604*cda5da8dSAndroid Build Coastguard Worker 605*cda5da8dSAndroid Build Coastguard Worker def date_time_string(self, timestamp=None): 606*cda5da8dSAndroid Build Coastguard Worker """Return the current date and time formatted for a message header.""" 607*cda5da8dSAndroid Build Coastguard Worker if timestamp is None: 608*cda5da8dSAndroid Build Coastguard Worker timestamp = time.time() 609*cda5da8dSAndroid Build Coastguard Worker return email.utils.formatdate(timestamp, usegmt=True) 610*cda5da8dSAndroid Build Coastguard Worker 611*cda5da8dSAndroid Build Coastguard Worker def log_date_time_string(self): 612*cda5da8dSAndroid Build Coastguard Worker """Return the current time formatted for logging.""" 613*cda5da8dSAndroid Build Coastguard Worker now = time.time() 614*cda5da8dSAndroid Build Coastguard Worker year, month, day, hh, mm, ss, x, y, z = time.localtime(now) 615*cda5da8dSAndroid Build Coastguard Worker s = "%02d/%3s/%04d %02d:%02d:%02d" % ( 616*cda5da8dSAndroid Build Coastguard Worker day, self.monthname[month], year, hh, mm, ss) 617*cda5da8dSAndroid Build Coastguard Worker return s 618*cda5da8dSAndroid Build Coastguard Worker 619*cda5da8dSAndroid Build Coastguard Worker weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 620*cda5da8dSAndroid Build Coastguard Worker 621*cda5da8dSAndroid Build Coastguard Worker monthname = [None, 622*cda5da8dSAndroid Build Coastguard Worker 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 623*cda5da8dSAndroid Build Coastguard Worker 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 624*cda5da8dSAndroid Build Coastguard Worker 625*cda5da8dSAndroid Build Coastguard Worker def address_string(self): 626*cda5da8dSAndroid Build Coastguard Worker """Return the client address.""" 627*cda5da8dSAndroid Build Coastguard Worker 628*cda5da8dSAndroid Build Coastguard Worker return self.client_address[0] 629*cda5da8dSAndroid Build Coastguard Worker 630*cda5da8dSAndroid Build Coastguard Worker # Essentially static class variables 631*cda5da8dSAndroid Build Coastguard Worker 632*cda5da8dSAndroid Build Coastguard Worker # The version of the HTTP protocol we support. 633*cda5da8dSAndroid Build Coastguard Worker # Set this to HTTP/1.1 to enable automatic keepalive 634*cda5da8dSAndroid Build Coastguard Worker protocol_version = "HTTP/1.0" 635*cda5da8dSAndroid Build Coastguard Worker 636*cda5da8dSAndroid Build Coastguard Worker # MessageClass used to parse headers 637*cda5da8dSAndroid Build Coastguard Worker MessageClass = http.client.HTTPMessage 638*cda5da8dSAndroid Build Coastguard Worker 639*cda5da8dSAndroid Build Coastguard Worker # hack to maintain backwards compatibility 640*cda5da8dSAndroid Build Coastguard Worker responses = { 641*cda5da8dSAndroid Build Coastguard Worker v: (v.phrase, v.description) 642*cda5da8dSAndroid Build Coastguard Worker for v in HTTPStatus.__members__.values() 643*cda5da8dSAndroid Build Coastguard Worker } 644*cda5da8dSAndroid Build Coastguard Worker 645*cda5da8dSAndroid Build Coastguard Worker 646*cda5da8dSAndroid Build Coastguard Workerclass SimpleHTTPRequestHandler(BaseHTTPRequestHandler): 647*cda5da8dSAndroid Build Coastguard Worker 648*cda5da8dSAndroid Build Coastguard Worker """Simple HTTP request handler with GET and HEAD commands. 649*cda5da8dSAndroid Build Coastguard Worker 650*cda5da8dSAndroid Build Coastguard Worker This serves files from the current directory and any of its 651*cda5da8dSAndroid Build Coastguard Worker subdirectories. The MIME type for files is determined by 652*cda5da8dSAndroid Build Coastguard Worker calling the .guess_type() method. 653*cda5da8dSAndroid Build Coastguard Worker 654*cda5da8dSAndroid Build Coastguard Worker The GET and HEAD requests are identical except that the HEAD 655*cda5da8dSAndroid Build Coastguard Worker request omits the actual contents of the file. 656*cda5da8dSAndroid Build Coastguard Worker 657*cda5da8dSAndroid Build Coastguard Worker """ 658*cda5da8dSAndroid Build Coastguard Worker 659*cda5da8dSAndroid Build Coastguard Worker server_version = "SimpleHTTP/" + __version__ 660*cda5da8dSAndroid Build Coastguard Worker extensions_map = _encodings_map_default = { 661*cda5da8dSAndroid Build Coastguard Worker '.gz': 'application/gzip', 662*cda5da8dSAndroid Build Coastguard Worker '.Z': 'application/octet-stream', 663*cda5da8dSAndroid Build Coastguard Worker '.bz2': 'application/x-bzip2', 664*cda5da8dSAndroid Build Coastguard Worker '.xz': 'application/x-xz', 665*cda5da8dSAndroid Build Coastguard Worker } 666*cda5da8dSAndroid Build Coastguard Worker 667*cda5da8dSAndroid Build Coastguard Worker def __init__(self, *args, directory=None, **kwargs): 668*cda5da8dSAndroid Build Coastguard Worker if directory is None: 669*cda5da8dSAndroid Build Coastguard Worker directory = os.getcwd() 670*cda5da8dSAndroid Build Coastguard Worker self.directory = os.fspath(directory) 671*cda5da8dSAndroid Build Coastguard Worker super().__init__(*args, **kwargs) 672*cda5da8dSAndroid Build Coastguard Worker 673*cda5da8dSAndroid Build Coastguard Worker def do_GET(self): 674*cda5da8dSAndroid Build Coastguard Worker """Serve a GET request.""" 675*cda5da8dSAndroid Build Coastguard Worker f = self.send_head() 676*cda5da8dSAndroid Build Coastguard Worker if f: 677*cda5da8dSAndroid Build Coastguard Worker try: 678*cda5da8dSAndroid Build Coastguard Worker self.copyfile(f, self.wfile) 679*cda5da8dSAndroid Build Coastguard Worker finally: 680*cda5da8dSAndroid Build Coastguard Worker f.close() 681*cda5da8dSAndroid Build Coastguard Worker 682*cda5da8dSAndroid Build Coastguard Worker def do_HEAD(self): 683*cda5da8dSAndroid Build Coastguard Worker """Serve a HEAD request.""" 684*cda5da8dSAndroid Build Coastguard Worker f = self.send_head() 685*cda5da8dSAndroid Build Coastguard Worker if f: 686*cda5da8dSAndroid Build Coastguard Worker f.close() 687*cda5da8dSAndroid Build Coastguard Worker 688*cda5da8dSAndroid Build Coastguard Worker def send_head(self): 689*cda5da8dSAndroid Build Coastguard Worker """Common code for GET and HEAD commands. 690*cda5da8dSAndroid Build Coastguard Worker 691*cda5da8dSAndroid Build Coastguard Worker This sends the response code and MIME headers. 692*cda5da8dSAndroid Build Coastguard Worker 693*cda5da8dSAndroid Build Coastguard Worker Return value is either a file object (which has to be copied 694*cda5da8dSAndroid Build Coastguard Worker to the outputfile by the caller unless the command was HEAD, 695*cda5da8dSAndroid Build Coastguard Worker and must be closed by the caller under all circumstances), or 696*cda5da8dSAndroid Build Coastguard Worker None, in which case the caller has nothing further to do. 697*cda5da8dSAndroid Build Coastguard Worker 698*cda5da8dSAndroid Build Coastguard Worker """ 699*cda5da8dSAndroid Build Coastguard Worker path = self.translate_path(self.path) 700*cda5da8dSAndroid Build Coastguard Worker f = None 701*cda5da8dSAndroid Build Coastguard Worker if os.path.isdir(path): 702*cda5da8dSAndroid Build Coastguard Worker parts = urllib.parse.urlsplit(self.path) 703*cda5da8dSAndroid Build Coastguard Worker if not parts.path.endswith('/'): 704*cda5da8dSAndroid Build Coastguard Worker # redirect browser - doing basically what apache does 705*cda5da8dSAndroid Build Coastguard Worker self.send_response(HTTPStatus.MOVED_PERMANENTLY) 706*cda5da8dSAndroid Build Coastguard Worker new_parts = (parts[0], parts[1], parts[2] + '/', 707*cda5da8dSAndroid Build Coastguard Worker parts[3], parts[4]) 708*cda5da8dSAndroid Build Coastguard Worker new_url = urllib.parse.urlunsplit(new_parts) 709*cda5da8dSAndroid Build Coastguard Worker self.send_header("Location", new_url) 710*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-Length", "0") 711*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 712*cda5da8dSAndroid Build Coastguard Worker return None 713*cda5da8dSAndroid Build Coastguard Worker for index in "index.html", "index.htm": 714*cda5da8dSAndroid Build Coastguard Worker index = os.path.join(path, index) 715*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(index): 716*cda5da8dSAndroid Build Coastguard Worker path = index 717*cda5da8dSAndroid Build Coastguard Worker break 718*cda5da8dSAndroid Build Coastguard Worker else: 719*cda5da8dSAndroid Build Coastguard Worker return self.list_directory(path) 720*cda5da8dSAndroid Build Coastguard Worker ctype = self.guess_type(path) 721*cda5da8dSAndroid Build Coastguard Worker # check for trailing "/" which should return 404. See Issue17324 722*cda5da8dSAndroid Build Coastguard Worker # The test for this was added in test_httpserver.py 723*cda5da8dSAndroid Build Coastguard Worker # However, some OS platforms accept a trailingSlash as a filename 724*cda5da8dSAndroid Build Coastguard Worker # See discussion on python-dev and Issue34711 regarding 725*cda5da8dSAndroid Build Coastguard Worker # parsing and rejection of filenames with a trailing slash 726*cda5da8dSAndroid Build Coastguard Worker if path.endswith("/"): 727*cda5da8dSAndroid Build Coastguard Worker self.send_error(HTTPStatus.NOT_FOUND, "File not found") 728*cda5da8dSAndroid Build Coastguard Worker return None 729*cda5da8dSAndroid Build Coastguard Worker try: 730*cda5da8dSAndroid Build Coastguard Worker f = open(path, 'rb') 731*cda5da8dSAndroid Build Coastguard Worker except OSError: 732*cda5da8dSAndroid Build Coastguard Worker self.send_error(HTTPStatus.NOT_FOUND, "File not found") 733*cda5da8dSAndroid Build Coastguard Worker return None 734*cda5da8dSAndroid Build Coastguard Worker 735*cda5da8dSAndroid Build Coastguard Worker try: 736*cda5da8dSAndroid Build Coastguard Worker fs = os.fstat(f.fileno()) 737*cda5da8dSAndroid Build Coastguard Worker # Use browser cache if possible 738*cda5da8dSAndroid Build Coastguard Worker if ("If-Modified-Since" in self.headers 739*cda5da8dSAndroid Build Coastguard Worker and "If-None-Match" not in self.headers): 740*cda5da8dSAndroid Build Coastguard Worker # compare If-Modified-Since and time of last file modification 741*cda5da8dSAndroid Build Coastguard Worker try: 742*cda5da8dSAndroid Build Coastguard Worker ims = email.utils.parsedate_to_datetime( 743*cda5da8dSAndroid Build Coastguard Worker self.headers["If-Modified-Since"]) 744*cda5da8dSAndroid Build Coastguard Worker except (TypeError, IndexError, OverflowError, ValueError): 745*cda5da8dSAndroid Build Coastguard Worker # ignore ill-formed values 746*cda5da8dSAndroid Build Coastguard Worker pass 747*cda5da8dSAndroid Build Coastguard Worker else: 748*cda5da8dSAndroid Build Coastguard Worker if ims.tzinfo is None: 749*cda5da8dSAndroid Build Coastguard Worker # obsolete format with no timezone, cf. 750*cda5da8dSAndroid Build Coastguard Worker # https://tools.ietf.org/html/rfc7231#section-7.1.1.1 751*cda5da8dSAndroid Build Coastguard Worker ims = ims.replace(tzinfo=datetime.timezone.utc) 752*cda5da8dSAndroid Build Coastguard Worker if ims.tzinfo is datetime.timezone.utc: 753*cda5da8dSAndroid Build Coastguard Worker # compare to UTC datetime of last modification 754*cda5da8dSAndroid Build Coastguard Worker last_modif = datetime.datetime.fromtimestamp( 755*cda5da8dSAndroid Build Coastguard Worker fs.st_mtime, datetime.timezone.utc) 756*cda5da8dSAndroid Build Coastguard Worker # remove microseconds, like in If-Modified-Since 757*cda5da8dSAndroid Build Coastguard Worker last_modif = last_modif.replace(microsecond=0) 758*cda5da8dSAndroid Build Coastguard Worker 759*cda5da8dSAndroid Build Coastguard Worker if last_modif <= ims: 760*cda5da8dSAndroid Build Coastguard Worker self.send_response(HTTPStatus.NOT_MODIFIED) 761*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 762*cda5da8dSAndroid Build Coastguard Worker f.close() 763*cda5da8dSAndroid Build Coastguard Worker return None 764*cda5da8dSAndroid Build Coastguard Worker 765*cda5da8dSAndroid Build Coastguard Worker self.send_response(HTTPStatus.OK) 766*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-type", ctype) 767*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-Length", str(fs[6])) 768*cda5da8dSAndroid Build Coastguard Worker self.send_header("Last-Modified", 769*cda5da8dSAndroid Build Coastguard Worker self.date_time_string(fs.st_mtime)) 770*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 771*cda5da8dSAndroid Build Coastguard Worker return f 772*cda5da8dSAndroid Build Coastguard Worker except: 773*cda5da8dSAndroid Build Coastguard Worker f.close() 774*cda5da8dSAndroid Build Coastguard Worker raise 775*cda5da8dSAndroid Build Coastguard Worker 776*cda5da8dSAndroid Build Coastguard Worker def list_directory(self, path): 777*cda5da8dSAndroid Build Coastguard Worker """Helper to produce a directory listing (absent index.html). 778*cda5da8dSAndroid Build Coastguard Worker 779*cda5da8dSAndroid Build Coastguard Worker Return value is either a file object, or None (indicating an 780*cda5da8dSAndroid Build Coastguard Worker error). In either case, the headers are sent, making the 781*cda5da8dSAndroid Build Coastguard Worker interface the same as for send_head(). 782*cda5da8dSAndroid Build Coastguard Worker 783*cda5da8dSAndroid Build Coastguard Worker """ 784*cda5da8dSAndroid Build Coastguard Worker try: 785*cda5da8dSAndroid Build Coastguard Worker list = os.listdir(path) 786*cda5da8dSAndroid Build Coastguard Worker except OSError: 787*cda5da8dSAndroid Build Coastguard Worker self.send_error( 788*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.NOT_FOUND, 789*cda5da8dSAndroid Build Coastguard Worker "No permission to list directory") 790*cda5da8dSAndroid Build Coastguard Worker return None 791*cda5da8dSAndroid Build Coastguard Worker list.sort(key=lambda a: a.lower()) 792*cda5da8dSAndroid Build Coastguard Worker r = [] 793*cda5da8dSAndroid Build Coastguard Worker try: 794*cda5da8dSAndroid Build Coastguard Worker displaypath = urllib.parse.unquote(self.path, 795*cda5da8dSAndroid Build Coastguard Worker errors='surrogatepass') 796*cda5da8dSAndroid Build Coastguard Worker except UnicodeDecodeError: 797*cda5da8dSAndroid Build Coastguard Worker displaypath = urllib.parse.unquote(self.path) 798*cda5da8dSAndroid Build Coastguard Worker displaypath = html.escape(displaypath, quote=False) 799*cda5da8dSAndroid Build Coastguard Worker enc = sys.getfilesystemencoding() 800*cda5da8dSAndroid Build Coastguard Worker title = f'Directory listing for {displaypath}' 801*cda5da8dSAndroid Build Coastguard Worker r.append('<!DOCTYPE HTML>') 802*cda5da8dSAndroid Build Coastguard Worker r.append('<html lang="en">') 803*cda5da8dSAndroid Build Coastguard Worker r.append('<head>') 804*cda5da8dSAndroid Build Coastguard Worker r.append(f'<meta charset="{enc}">') 805*cda5da8dSAndroid Build Coastguard Worker r.append(f'<title>{title}</title>\n</head>') 806*cda5da8dSAndroid Build Coastguard Worker r.append(f'<body>\n<h1>{title}</h1>') 807*cda5da8dSAndroid Build Coastguard Worker r.append('<hr>\n<ul>') 808*cda5da8dSAndroid Build Coastguard Worker for name in list: 809*cda5da8dSAndroid Build Coastguard Worker fullname = os.path.join(path, name) 810*cda5da8dSAndroid Build Coastguard Worker displayname = linkname = name 811*cda5da8dSAndroid Build Coastguard Worker # Append / for directories or @ for symbolic links 812*cda5da8dSAndroid Build Coastguard Worker if os.path.isdir(fullname): 813*cda5da8dSAndroid Build Coastguard Worker displayname = name + "/" 814*cda5da8dSAndroid Build Coastguard Worker linkname = name + "/" 815*cda5da8dSAndroid Build Coastguard Worker if os.path.islink(fullname): 816*cda5da8dSAndroid Build Coastguard Worker displayname = name + "@" 817*cda5da8dSAndroid Build Coastguard Worker # Note: a link to a directory displays with @ and links with / 818*cda5da8dSAndroid Build Coastguard Worker r.append('<li><a href="%s">%s</a></li>' 819*cda5da8dSAndroid Build Coastguard Worker % (urllib.parse.quote(linkname, 820*cda5da8dSAndroid Build Coastguard Worker errors='surrogatepass'), 821*cda5da8dSAndroid Build Coastguard Worker html.escape(displayname, quote=False))) 822*cda5da8dSAndroid Build Coastguard Worker r.append('</ul>\n<hr>\n</body>\n</html>\n') 823*cda5da8dSAndroid Build Coastguard Worker encoded = '\n'.join(r).encode(enc, 'surrogateescape') 824*cda5da8dSAndroid Build Coastguard Worker f = io.BytesIO() 825*cda5da8dSAndroid Build Coastguard Worker f.write(encoded) 826*cda5da8dSAndroid Build Coastguard Worker f.seek(0) 827*cda5da8dSAndroid Build Coastguard Worker self.send_response(HTTPStatus.OK) 828*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-type", "text/html; charset=%s" % enc) 829*cda5da8dSAndroid Build Coastguard Worker self.send_header("Content-Length", str(len(encoded))) 830*cda5da8dSAndroid Build Coastguard Worker self.end_headers() 831*cda5da8dSAndroid Build Coastguard Worker return f 832*cda5da8dSAndroid Build Coastguard Worker 833*cda5da8dSAndroid Build Coastguard Worker def translate_path(self, path): 834*cda5da8dSAndroid Build Coastguard Worker """Translate a /-separated PATH to the local filename syntax. 835*cda5da8dSAndroid Build Coastguard Worker 836*cda5da8dSAndroid Build Coastguard Worker Components that mean special things to the local file system 837*cda5da8dSAndroid Build Coastguard Worker (e.g. drive or directory names) are ignored. (XXX They should 838*cda5da8dSAndroid Build Coastguard Worker probably be diagnosed.) 839*cda5da8dSAndroid Build Coastguard Worker 840*cda5da8dSAndroid Build Coastguard Worker """ 841*cda5da8dSAndroid Build Coastguard Worker # abandon query parameters 842*cda5da8dSAndroid Build Coastguard Worker path = path.split('?',1)[0] 843*cda5da8dSAndroid Build Coastguard Worker path = path.split('#',1)[0] 844*cda5da8dSAndroid Build Coastguard Worker # Don't forget explicit trailing slash when normalizing. Issue17324 845*cda5da8dSAndroid Build Coastguard Worker trailing_slash = path.rstrip().endswith('/') 846*cda5da8dSAndroid Build Coastguard Worker try: 847*cda5da8dSAndroid Build Coastguard Worker path = urllib.parse.unquote(path, errors='surrogatepass') 848*cda5da8dSAndroid Build Coastguard Worker except UnicodeDecodeError: 849*cda5da8dSAndroid Build Coastguard Worker path = urllib.parse.unquote(path) 850*cda5da8dSAndroid Build Coastguard Worker path = posixpath.normpath(path) 851*cda5da8dSAndroid Build Coastguard Worker words = path.split('/') 852*cda5da8dSAndroid Build Coastguard Worker words = filter(None, words) 853*cda5da8dSAndroid Build Coastguard Worker path = self.directory 854*cda5da8dSAndroid Build Coastguard Worker for word in words: 855*cda5da8dSAndroid Build Coastguard Worker if os.path.dirname(word) or word in (os.curdir, os.pardir): 856*cda5da8dSAndroid Build Coastguard Worker # Ignore components that are not a simple file/directory name 857*cda5da8dSAndroid Build Coastguard Worker continue 858*cda5da8dSAndroid Build Coastguard Worker path = os.path.join(path, word) 859*cda5da8dSAndroid Build Coastguard Worker if trailing_slash: 860*cda5da8dSAndroid Build Coastguard Worker path += '/' 861*cda5da8dSAndroid Build Coastguard Worker return path 862*cda5da8dSAndroid Build Coastguard Worker 863*cda5da8dSAndroid Build Coastguard Worker def copyfile(self, source, outputfile): 864*cda5da8dSAndroid Build Coastguard Worker """Copy all data between two file objects. 865*cda5da8dSAndroid Build Coastguard Worker 866*cda5da8dSAndroid Build Coastguard Worker The SOURCE argument is a file object open for reading 867*cda5da8dSAndroid Build Coastguard Worker (or anything with a read() method) and the DESTINATION 868*cda5da8dSAndroid Build Coastguard Worker argument is a file object open for writing (or 869*cda5da8dSAndroid Build Coastguard Worker anything with a write() method). 870*cda5da8dSAndroid Build Coastguard Worker 871*cda5da8dSAndroid Build Coastguard Worker The only reason for overriding this would be to change 872*cda5da8dSAndroid Build Coastguard Worker the block size or perhaps to replace newlines by CRLF 873*cda5da8dSAndroid Build Coastguard Worker -- note however that this the default server uses this 874*cda5da8dSAndroid Build Coastguard Worker to copy binary data as well. 875*cda5da8dSAndroid Build Coastguard Worker 876*cda5da8dSAndroid Build Coastguard Worker """ 877*cda5da8dSAndroid Build Coastguard Worker shutil.copyfileobj(source, outputfile) 878*cda5da8dSAndroid Build Coastguard Worker 879*cda5da8dSAndroid Build Coastguard Worker def guess_type(self, path): 880*cda5da8dSAndroid Build Coastguard Worker """Guess the type of a file. 881*cda5da8dSAndroid Build Coastguard Worker 882*cda5da8dSAndroid Build Coastguard Worker Argument is a PATH (a filename). 883*cda5da8dSAndroid Build Coastguard Worker 884*cda5da8dSAndroid Build Coastguard Worker Return value is a string of the form type/subtype, 885*cda5da8dSAndroid Build Coastguard Worker usable for a MIME Content-type header. 886*cda5da8dSAndroid Build Coastguard Worker 887*cda5da8dSAndroid Build Coastguard Worker The default implementation looks the file's extension 888*cda5da8dSAndroid Build Coastguard Worker up in the table self.extensions_map, using application/octet-stream 889*cda5da8dSAndroid Build Coastguard Worker as a default; however it would be permissible (if 890*cda5da8dSAndroid Build Coastguard Worker slow) to look inside the data to make a better guess. 891*cda5da8dSAndroid Build Coastguard Worker 892*cda5da8dSAndroid Build Coastguard Worker """ 893*cda5da8dSAndroid Build Coastguard Worker base, ext = posixpath.splitext(path) 894*cda5da8dSAndroid Build Coastguard Worker if ext in self.extensions_map: 895*cda5da8dSAndroid Build Coastguard Worker return self.extensions_map[ext] 896*cda5da8dSAndroid Build Coastguard Worker ext = ext.lower() 897*cda5da8dSAndroid Build Coastguard Worker if ext in self.extensions_map: 898*cda5da8dSAndroid Build Coastguard Worker return self.extensions_map[ext] 899*cda5da8dSAndroid Build Coastguard Worker guess, _ = mimetypes.guess_type(path) 900*cda5da8dSAndroid Build Coastguard Worker if guess: 901*cda5da8dSAndroid Build Coastguard Worker return guess 902*cda5da8dSAndroid Build Coastguard Worker return 'application/octet-stream' 903*cda5da8dSAndroid Build Coastguard Worker 904*cda5da8dSAndroid Build Coastguard Worker 905*cda5da8dSAndroid Build Coastguard Worker# Utilities for CGIHTTPRequestHandler 906*cda5da8dSAndroid Build Coastguard Worker 907*cda5da8dSAndroid Build Coastguard Workerdef _url_collapse_path(path): 908*cda5da8dSAndroid Build Coastguard Worker """ 909*cda5da8dSAndroid Build Coastguard Worker Given a URL path, remove extra '/'s and '.' path elements and collapse 910*cda5da8dSAndroid Build Coastguard Worker any '..' references and returns a collapsed path. 911*cda5da8dSAndroid Build Coastguard Worker 912*cda5da8dSAndroid Build Coastguard Worker Implements something akin to RFC-2396 5.2 step 6 to parse relative paths. 913*cda5da8dSAndroid Build Coastguard Worker The utility of this function is limited to is_cgi method and helps 914*cda5da8dSAndroid Build Coastguard Worker preventing some security attacks. 915*cda5da8dSAndroid Build Coastguard Worker 916*cda5da8dSAndroid Build Coastguard Worker Returns: The reconstituted URL, which will always start with a '/'. 917*cda5da8dSAndroid Build Coastguard Worker 918*cda5da8dSAndroid Build Coastguard Worker Raises: IndexError if too many '..' occur within the path. 919*cda5da8dSAndroid Build Coastguard Worker 920*cda5da8dSAndroid Build Coastguard Worker """ 921*cda5da8dSAndroid Build Coastguard Worker # Query component should not be involved. 922*cda5da8dSAndroid Build Coastguard Worker path, _, query = path.partition('?') 923*cda5da8dSAndroid Build Coastguard Worker path = urllib.parse.unquote(path) 924*cda5da8dSAndroid Build Coastguard Worker 925*cda5da8dSAndroid Build Coastguard Worker # Similar to os.path.split(os.path.normpath(path)) but specific to URL 926*cda5da8dSAndroid Build Coastguard Worker # path semantics rather than local operating system semantics. 927*cda5da8dSAndroid Build Coastguard Worker path_parts = path.split('/') 928*cda5da8dSAndroid Build Coastguard Worker head_parts = [] 929*cda5da8dSAndroid Build Coastguard Worker for part in path_parts[:-1]: 930*cda5da8dSAndroid Build Coastguard Worker if part == '..': 931*cda5da8dSAndroid Build Coastguard Worker head_parts.pop() # IndexError if more '..' than prior parts 932*cda5da8dSAndroid Build Coastguard Worker elif part and part != '.': 933*cda5da8dSAndroid Build Coastguard Worker head_parts.append( part ) 934*cda5da8dSAndroid Build Coastguard Worker if path_parts: 935*cda5da8dSAndroid Build Coastguard Worker tail_part = path_parts.pop() 936*cda5da8dSAndroid Build Coastguard Worker if tail_part: 937*cda5da8dSAndroid Build Coastguard Worker if tail_part == '..': 938*cda5da8dSAndroid Build Coastguard Worker head_parts.pop() 939*cda5da8dSAndroid Build Coastguard Worker tail_part = '' 940*cda5da8dSAndroid Build Coastguard Worker elif tail_part == '.': 941*cda5da8dSAndroid Build Coastguard Worker tail_part = '' 942*cda5da8dSAndroid Build Coastguard Worker else: 943*cda5da8dSAndroid Build Coastguard Worker tail_part = '' 944*cda5da8dSAndroid Build Coastguard Worker 945*cda5da8dSAndroid Build Coastguard Worker if query: 946*cda5da8dSAndroid Build Coastguard Worker tail_part = '?'.join((tail_part, query)) 947*cda5da8dSAndroid Build Coastguard Worker 948*cda5da8dSAndroid Build Coastguard Worker splitpath = ('/' + '/'.join(head_parts), tail_part) 949*cda5da8dSAndroid Build Coastguard Worker collapsed_path = "/".join(splitpath) 950*cda5da8dSAndroid Build Coastguard Worker 951*cda5da8dSAndroid Build Coastguard Worker return collapsed_path 952*cda5da8dSAndroid Build Coastguard Worker 953*cda5da8dSAndroid Build Coastguard Worker 954*cda5da8dSAndroid Build Coastguard Worker 955*cda5da8dSAndroid Build Coastguard Workernobody = None 956*cda5da8dSAndroid Build Coastguard Worker 957*cda5da8dSAndroid Build Coastguard Workerdef nobody_uid(): 958*cda5da8dSAndroid Build Coastguard Worker """Internal routine to get nobody's uid""" 959*cda5da8dSAndroid Build Coastguard Worker global nobody 960*cda5da8dSAndroid Build Coastguard Worker if nobody: 961*cda5da8dSAndroid Build Coastguard Worker return nobody 962*cda5da8dSAndroid Build Coastguard Worker try: 963*cda5da8dSAndroid Build Coastguard Worker import pwd 964*cda5da8dSAndroid Build Coastguard Worker except ImportError: 965*cda5da8dSAndroid Build Coastguard Worker return -1 966*cda5da8dSAndroid Build Coastguard Worker try: 967*cda5da8dSAndroid Build Coastguard Worker nobody = pwd.getpwnam('nobody')[2] 968*cda5da8dSAndroid Build Coastguard Worker except KeyError: 969*cda5da8dSAndroid Build Coastguard Worker nobody = 1 + max(x[2] for x in pwd.getpwall()) 970*cda5da8dSAndroid Build Coastguard Worker return nobody 971*cda5da8dSAndroid Build Coastguard Worker 972*cda5da8dSAndroid Build Coastguard Worker 973*cda5da8dSAndroid Build Coastguard Workerdef executable(path): 974*cda5da8dSAndroid Build Coastguard Worker """Test for executable file.""" 975*cda5da8dSAndroid Build Coastguard Worker return os.access(path, os.X_OK) 976*cda5da8dSAndroid Build Coastguard Worker 977*cda5da8dSAndroid Build Coastguard Worker 978*cda5da8dSAndroid Build Coastguard Workerclass CGIHTTPRequestHandler(SimpleHTTPRequestHandler): 979*cda5da8dSAndroid Build Coastguard Worker 980*cda5da8dSAndroid Build Coastguard Worker """Complete HTTP server with GET, HEAD and POST commands. 981*cda5da8dSAndroid Build Coastguard Worker 982*cda5da8dSAndroid Build Coastguard Worker GET and HEAD also support running CGI scripts. 983*cda5da8dSAndroid Build Coastguard Worker 984*cda5da8dSAndroid Build Coastguard Worker The POST command is *only* implemented for CGI scripts. 985*cda5da8dSAndroid Build Coastguard Worker 986*cda5da8dSAndroid Build Coastguard Worker """ 987*cda5da8dSAndroid Build Coastguard Worker 988*cda5da8dSAndroid Build Coastguard Worker # Determine platform specifics 989*cda5da8dSAndroid Build Coastguard Worker have_fork = hasattr(os, 'fork') 990*cda5da8dSAndroid Build Coastguard Worker 991*cda5da8dSAndroid Build Coastguard Worker # Make rfile unbuffered -- we need to read one line and then pass 992*cda5da8dSAndroid Build Coastguard Worker # the rest to a subprocess, so we can't use buffered input. 993*cda5da8dSAndroid Build Coastguard Worker rbufsize = 0 994*cda5da8dSAndroid Build Coastguard Worker 995*cda5da8dSAndroid Build Coastguard Worker def do_POST(self): 996*cda5da8dSAndroid Build Coastguard Worker """Serve a POST request. 997*cda5da8dSAndroid Build Coastguard Worker 998*cda5da8dSAndroid Build Coastguard Worker This is only implemented for CGI scripts. 999*cda5da8dSAndroid Build Coastguard Worker 1000*cda5da8dSAndroid Build Coastguard Worker """ 1001*cda5da8dSAndroid Build Coastguard Worker 1002*cda5da8dSAndroid Build Coastguard Worker if self.is_cgi(): 1003*cda5da8dSAndroid Build Coastguard Worker self.run_cgi() 1004*cda5da8dSAndroid Build Coastguard Worker else: 1005*cda5da8dSAndroid Build Coastguard Worker self.send_error( 1006*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.NOT_IMPLEMENTED, 1007*cda5da8dSAndroid Build Coastguard Worker "Can only POST to CGI scripts") 1008*cda5da8dSAndroid Build Coastguard Worker 1009*cda5da8dSAndroid Build Coastguard Worker def send_head(self): 1010*cda5da8dSAndroid Build Coastguard Worker """Version of send_head that support CGI scripts""" 1011*cda5da8dSAndroid Build Coastguard Worker if self.is_cgi(): 1012*cda5da8dSAndroid Build Coastguard Worker return self.run_cgi() 1013*cda5da8dSAndroid Build Coastguard Worker else: 1014*cda5da8dSAndroid Build Coastguard Worker return SimpleHTTPRequestHandler.send_head(self) 1015*cda5da8dSAndroid Build Coastguard Worker 1016*cda5da8dSAndroid Build Coastguard Worker def is_cgi(self): 1017*cda5da8dSAndroid Build Coastguard Worker """Test whether self.path corresponds to a CGI script. 1018*cda5da8dSAndroid Build Coastguard Worker 1019*cda5da8dSAndroid Build Coastguard Worker Returns True and updates the cgi_info attribute to the tuple 1020*cda5da8dSAndroid Build Coastguard Worker (dir, rest) if self.path requires running a CGI script. 1021*cda5da8dSAndroid Build Coastguard Worker Returns False otherwise. 1022*cda5da8dSAndroid Build Coastguard Worker 1023*cda5da8dSAndroid Build Coastguard Worker If any exception is raised, the caller should assume that 1024*cda5da8dSAndroid Build Coastguard Worker self.path was rejected as invalid and act accordingly. 1025*cda5da8dSAndroid Build Coastguard Worker 1026*cda5da8dSAndroid Build Coastguard Worker The default implementation tests whether the normalized url 1027*cda5da8dSAndroid Build Coastguard Worker path begins with one of the strings in self.cgi_directories 1028*cda5da8dSAndroid Build Coastguard Worker (and the next character is a '/' or the end of the string). 1029*cda5da8dSAndroid Build Coastguard Worker 1030*cda5da8dSAndroid Build Coastguard Worker """ 1031*cda5da8dSAndroid Build Coastguard Worker collapsed_path = _url_collapse_path(self.path) 1032*cda5da8dSAndroid Build Coastguard Worker dir_sep = collapsed_path.find('/', 1) 1033*cda5da8dSAndroid Build Coastguard Worker while dir_sep > 0 and not collapsed_path[:dir_sep] in self.cgi_directories: 1034*cda5da8dSAndroid Build Coastguard Worker dir_sep = collapsed_path.find('/', dir_sep+1) 1035*cda5da8dSAndroid Build Coastguard Worker if dir_sep > 0: 1036*cda5da8dSAndroid Build Coastguard Worker head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] 1037*cda5da8dSAndroid Build Coastguard Worker self.cgi_info = head, tail 1038*cda5da8dSAndroid Build Coastguard Worker return True 1039*cda5da8dSAndroid Build Coastguard Worker return False 1040*cda5da8dSAndroid Build Coastguard Worker 1041*cda5da8dSAndroid Build Coastguard Worker 1042*cda5da8dSAndroid Build Coastguard Worker cgi_directories = ['/cgi-bin', '/htbin'] 1043*cda5da8dSAndroid Build Coastguard Worker 1044*cda5da8dSAndroid Build Coastguard Worker def is_executable(self, path): 1045*cda5da8dSAndroid Build Coastguard Worker """Test whether argument path is an executable file.""" 1046*cda5da8dSAndroid Build Coastguard Worker return executable(path) 1047*cda5da8dSAndroid Build Coastguard Worker 1048*cda5da8dSAndroid Build Coastguard Worker def is_python(self, path): 1049*cda5da8dSAndroid Build Coastguard Worker """Test whether argument path is a Python script.""" 1050*cda5da8dSAndroid Build Coastguard Worker head, tail = os.path.splitext(path) 1051*cda5da8dSAndroid Build Coastguard Worker return tail.lower() in (".py", ".pyw") 1052*cda5da8dSAndroid Build Coastguard Worker 1053*cda5da8dSAndroid Build Coastguard Worker def run_cgi(self): 1054*cda5da8dSAndroid Build Coastguard Worker """Execute a CGI script.""" 1055*cda5da8dSAndroid Build Coastguard Worker dir, rest = self.cgi_info 1056*cda5da8dSAndroid Build Coastguard Worker path = dir + '/' + rest 1057*cda5da8dSAndroid Build Coastguard Worker i = path.find('/', len(dir)+1) 1058*cda5da8dSAndroid Build Coastguard Worker while i >= 0: 1059*cda5da8dSAndroid Build Coastguard Worker nextdir = path[:i] 1060*cda5da8dSAndroid Build Coastguard Worker nextrest = path[i+1:] 1061*cda5da8dSAndroid Build Coastguard Worker 1062*cda5da8dSAndroid Build Coastguard Worker scriptdir = self.translate_path(nextdir) 1063*cda5da8dSAndroid Build Coastguard Worker if os.path.isdir(scriptdir): 1064*cda5da8dSAndroid Build Coastguard Worker dir, rest = nextdir, nextrest 1065*cda5da8dSAndroid Build Coastguard Worker i = path.find('/', len(dir)+1) 1066*cda5da8dSAndroid Build Coastguard Worker else: 1067*cda5da8dSAndroid Build Coastguard Worker break 1068*cda5da8dSAndroid Build Coastguard Worker 1069*cda5da8dSAndroid Build Coastguard Worker # find an explicit query string, if present. 1070*cda5da8dSAndroid Build Coastguard Worker rest, _, query = rest.partition('?') 1071*cda5da8dSAndroid Build Coastguard Worker 1072*cda5da8dSAndroid Build Coastguard Worker # dissect the part after the directory name into a script name & 1073*cda5da8dSAndroid Build Coastguard Worker # a possible additional path, to be stored in PATH_INFO. 1074*cda5da8dSAndroid Build Coastguard Worker i = rest.find('/') 1075*cda5da8dSAndroid Build Coastguard Worker if i >= 0: 1076*cda5da8dSAndroid Build Coastguard Worker script, rest = rest[:i], rest[i:] 1077*cda5da8dSAndroid Build Coastguard Worker else: 1078*cda5da8dSAndroid Build Coastguard Worker script, rest = rest, '' 1079*cda5da8dSAndroid Build Coastguard Worker 1080*cda5da8dSAndroid Build Coastguard Worker scriptname = dir + '/' + script 1081*cda5da8dSAndroid Build Coastguard Worker scriptfile = self.translate_path(scriptname) 1082*cda5da8dSAndroid Build Coastguard Worker if not os.path.exists(scriptfile): 1083*cda5da8dSAndroid Build Coastguard Worker self.send_error( 1084*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.NOT_FOUND, 1085*cda5da8dSAndroid Build Coastguard Worker "No such CGI script (%r)" % scriptname) 1086*cda5da8dSAndroid Build Coastguard Worker return 1087*cda5da8dSAndroid Build Coastguard Worker if not os.path.isfile(scriptfile): 1088*cda5da8dSAndroid Build Coastguard Worker self.send_error( 1089*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.FORBIDDEN, 1090*cda5da8dSAndroid Build Coastguard Worker "CGI script is not a plain file (%r)" % scriptname) 1091*cda5da8dSAndroid Build Coastguard Worker return 1092*cda5da8dSAndroid Build Coastguard Worker ispy = self.is_python(scriptname) 1093*cda5da8dSAndroid Build Coastguard Worker if self.have_fork or not ispy: 1094*cda5da8dSAndroid Build Coastguard Worker if not self.is_executable(scriptfile): 1095*cda5da8dSAndroid Build Coastguard Worker self.send_error( 1096*cda5da8dSAndroid Build Coastguard Worker HTTPStatus.FORBIDDEN, 1097*cda5da8dSAndroid Build Coastguard Worker "CGI script is not executable (%r)" % scriptname) 1098*cda5da8dSAndroid Build Coastguard Worker return 1099*cda5da8dSAndroid Build Coastguard Worker 1100*cda5da8dSAndroid Build Coastguard Worker # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html 1101*cda5da8dSAndroid Build Coastguard Worker # XXX Much of the following could be prepared ahead of time! 1102*cda5da8dSAndroid Build Coastguard Worker env = copy.deepcopy(os.environ) 1103*cda5da8dSAndroid Build Coastguard Worker env['SERVER_SOFTWARE'] = self.version_string() 1104*cda5da8dSAndroid Build Coastguard Worker env['SERVER_NAME'] = self.server.server_name 1105*cda5da8dSAndroid Build Coastguard Worker env['GATEWAY_INTERFACE'] = 'CGI/1.1' 1106*cda5da8dSAndroid Build Coastguard Worker env['SERVER_PROTOCOL'] = self.protocol_version 1107*cda5da8dSAndroid Build Coastguard Worker env['SERVER_PORT'] = str(self.server.server_port) 1108*cda5da8dSAndroid Build Coastguard Worker env['REQUEST_METHOD'] = self.command 1109*cda5da8dSAndroid Build Coastguard Worker uqrest = urllib.parse.unquote(rest) 1110*cda5da8dSAndroid Build Coastguard Worker env['PATH_INFO'] = uqrest 1111*cda5da8dSAndroid Build Coastguard Worker env['PATH_TRANSLATED'] = self.translate_path(uqrest) 1112*cda5da8dSAndroid Build Coastguard Worker env['SCRIPT_NAME'] = scriptname 1113*cda5da8dSAndroid Build Coastguard Worker env['QUERY_STRING'] = query 1114*cda5da8dSAndroid Build Coastguard Worker env['REMOTE_ADDR'] = self.client_address[0] 1115*cda5da8dSAndroid Build Coastguard Worker authorization = self.headers.get("authorization") 1116*cda5da8dSAndroid Build Coastguard Worker if authorization: 1117*cda5da8dSAndroid Build Coastguard Worker authorization = authorization.split() 1118*cda5da8dSAndroid Build Coastguard Worker if len(authorization) == 2: 1119*cda5da8dSAndroid Build Coastguard Worker import base64, binascii 1120*cda5da8dSAndroid Build Coastguard Worker env['AUTH_TYPE'] = authorization[0] 1121*cda5da8dSAndroid Build Coastguard Worker if authorization[0].lower() == "basic": 1122*cda5da8dSAndroid Build Coastguard Worker try: 1123*cda5da8dSAndroid Build Coastguard Worker authorization = authorization[1].encode('ascii') 1124*cda5da8dSAndroid Build Coastguard Worker authorization = base64.decodebytes(authorization).\ 1125*cda5da8dSAndroid Build Coastguard Worker decode('ascii') 1126*cda5da8dSAndroid Build Coastguard Worker except (binascii.Error, UnicodeError): 1127*cda5da8dSAndroid Build Coastguard Worker pass 1128*cda5da8dSAndroid Build Coastguard Worker else: 1129*cda5da8dSAndroid Build Coastguard Worker authorization = authorization.split(':') 1130*cda5da8dSAndroid Build Coastguard Worker if len(authorization) == 2: 1131*cda5da8dSAndroid Build Coastguard Worker env['REMOTE_USER'] = authorization[0] 1132*cda5da8dSAndroid Build Coastguard Worker # XXX REMOTE_IDENT 1133*cda5da8dSAndroid Build Coastguard Worker if self.headers.get('content-type') is None: 1134*cda5da8dSAndroid Build Coastguard Worker env['CONTENT_TYPE'] = self.headers.get_content_type() 1135*cda5da8dSAndroid Build Coastguard Worker else: 1136*cda5da8dSAndroid Build Coastguard Worker env['CONTENT_TYPE'] = self.headers['content-type'] 1137*cda5da8dSAndroid Build Coastguard Worker length = self.headers.get('content-length') 1138*cda5da8dSAndroid Build Coastguard Worker if length: 1139*cda5da8dSAndroid Build Coastguard Worker env['CONTENT_LENGTH'] = length 1140*cda5da8dSAndroid Build Coastguard Worker referer = self.headers.get('referer') 1141*cda5da8dSAndroid Build Coastguard Worker if referer: 1142*cda5da8dSAndroid Build Coastguard Worker env['HTTP_REFERER'] = referer 1143*cda5da8dSAndroid Build Coastguard Worker accept = self.headers.get_all('accept', ()) 1144*cda5da8dSAndroid Build Coastguard Worker env['HTTP_ACCEPT'] = ','.join(accept) 1145*cda5da8dSAndroid Build Coastguard Worker ua = self.headers.get('user-agent') 1146*cda5da8dSAndroid Build Coastguard Worker if ua: 1147*cda5da8dSAndroid Build Coastguard Worker env['HTTP_USER_AGENT'] = ua 1148*cda5da8dSAndroid Build Coastguard Worker co = filter(None, self.headers.get_all('cookie', [])) 1149*cda5da8dSAndroid Build Coastguard Worker cookie_str = ', '.join(co) 1150*cda5da8dSAndroid Build Coastguard Worker if cookie_str: 1151*cda5da8dSAndroid Build Coastguard Worker env['HTTP_COOKIE'] = cookie_str 1152*cda5da8dSAndroid Build Coastguard Worker # XXX Other HTTP_* headers 1153*cda5da8dSAndroid Build Coastguard Worker # Since we're setting the env in the parent, provide empty 1154*cda5da8dSAndroid Build Coastguard Worker # values to override previously set values 1155*cda5da8dSAndroid Build Coastguard Worker for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', 1156*cda5da8dSAndroid Build Coastguard Worker 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): 1157*cda5da8dSAndroid Build Coastguard Worker env.setdefault(k, "") 1158*cda5da8dSAndroid Build Coastguard Worker 1159*cda5da8dSAndroid Build Coastguard Worker self.send_response(HTTPStatus.OK, "Script output follows") 1160*cda5da8dSAndroid Build Coastguard Worker self.flush_headers() 1161*cda5da8dSAndroid Build Coastguard Worker 1162*cda5da8dSAndroid Build Coastguard Worker decoded_query = query.replace('+', ' ') 1163*cda5da8dSAndroid Build Coastguard Worker 1164*cda5da8dSAndroid Build Coastguard Worker if self.have_fork: 1165*cda5da8dSAndroid Build Coastguard Worker # Unix -- fork as we should 1166*cda5da8dSAndroid Build Coastguard Worker args = [script] 1167*cda5da8dSAndroid Build Coastguard Worker if '=' not in decoded_query: 1168*cda5da8dSAndroid Build Coastguard Worker args.append(decoded_query) 1169*cda5da8dSAndroid Build Coastguard Worker nobody = nobody_uid() 1170*cda5da8dSAndroid Build Coastguard Worker self.wfile.flush() # Always flush before forking 1171*cda5da8dSAndroid Build Coastguard Worker pid = os.fork() 1172*cda5da8dSAndroid Build Coastguard Worker if pid != 0: 1173*cda5da8dSAndroid Build Coastguard Worker # Parent 1174*cda5da8dSAndroid Build Coastguard Worker pid, sts = os.waitpid(pid, 0) 1175*cda5da8dSAndroid Build Coastguard Worker # throw away additional data [see bug #427345] 1176*cda5da8dSAndroid Build Coastguard Worker while select.select([self.rfile], [], [], 0)[0]: 1177*cda5da8dSAndroid Build Coastguard Worker if not self.rfile.read(1): 1178*cda5da8dSAndroid Build Coastguard Worker break 1179*cda5da8dSAndroid Build Coastguard Worker exitcode = os.waitstatus_to_exitcode(sts) 1180*cda5da8dSAndroid Build Coastguard Worker if exitcode: 1181*cda5da8dSAndroid Build Coastguard Worker self.log_error(f"CGI script exit code {exitcode}") 1182*cda5da8dSAndroid Build Coastguard Worker return 1183*cda5da8dSAndroid Build Coastguard Worker # Child 1184*cda5da8dSAndroid Build Coastguard Worker try: 1185*cda5da8dSAndroid Build Coastguard Worker try: 1186*cda5da8dSAndroid Build Coastguard Worker os.setuid(nobody) 1187*cda5da8dSAndroid Build Coastguard Worker except OSError: 1188*cda5da8dSAndroid Build Coastguard Worker pass 1189*cda5da8dSAndroid Build Coastguard Worker os.dup2(self.rfile.fileno(), 0) 1190*cda5da8dSAndroid Build Coastguard Worker os.dup2(self.wfile.fileno(), 1) 1191*cda5da8dSAndroid Build Coastguard Worker os.execve(scriptfile, args, env) 1192*cda5da8dSAndroid Build Coastguard Worker except: 1193*cda5da8dSAndroid Build Coastguard Worker self.server.handle_error(self.request, self.client_address) 1194*cda5da8dSAndroid Build Coastguard Worker os._exit(127) 1195*cda5da8dSAndroid Build Coastguard Worker 1196*cda5da8dSAndroid Build Coastguard Worker else: 1197*cda5da8dSAndroid Build Coastguard Worker # Non-Unix -- use subprocess 1198*cda5da8dSAndroid Build Coastguard Worker import subprocess 1199*cda5da8dSAndroid Build Coastguard Worker cmdline = [scriptfile] 1200*cda5da8dSAndroid Build Coastguard Worker if self.is_python(scriptfile): 1201*cda5da8dSAndroid Build Coastguard Worker interp = sys.executable 1202*cda5da8dSAndroid Build Coastguard Worker if interp.lower().endswith("w.exe"): 1203*cda5da8dSAndroid Build Coastguard Worker # On Windows, use python.exe, not pythonw.exe 1204*cda5da8dSAndroid Build Coastguard Worker interp = interp[:-5] + interp[-4:] 1205*cda5da8dSAndroid Build Coastguard Worker cmdline = [interp, '-u'] + cmdline 1206*cda5da8dSAndroid Build Coastguard Worker if '=' not in query: 1207*cda5da8dSAndroid Build Coastguard Worker cmdline.append(query) 1208*cda5da8dSAndroid Build Coastguard Worker self.log_message("command: %s", subprocess.list2cmdline(cmdline)) 1209*cda5da8dSAndroid Build Coastguard Worker try: 1210*cda5da8dSAndroid Build Coastguard Worker nbytes = int(length) 1211*cda5da8dSAndroid Build Coastguard Worker except (TypeError, ValueError): 1212*cda5da8dSAndroid Build Coastguard Worker nbytes = 0 1213*cda5da8dSAndroid Build Coastguard Worker p = subprocess.Popen(cmdline, 1214*cda5da8dSAndroid Build Coastguard Worker stdin=subprocess.PIPE, 1215*cda5da8dSAndroid Build Coastguard Worker stdout=subprocess.PIPE, 1216*cda5da8dSAndroid Build Coastguard Worker stderr=subprocess.PIPE, 1217*cda5da8dSAndroid Build Coastguard Worker env = env 1218*cda5da8dSAndroid Build Coastguard Worker ) 1219*cda5da8dSAndroid Build Coastguard Worker if self.command.lower() == "post" and nbytes > 0: 1220*cda5da8dSAndroid Build Coastguard Worker data = self.rfile.read(nbytes) 1221*cda5da8dSAndroid Build Coastguard Worker else: 1222*cda5da8dSAndroid Build Coastguard Worker data = None 1223*cda5da8dSAndroid Build Coastguard Worker # throw away additional data [see bug #427345] 1224*cda5da8dSAndroid Build Coastguard Worker while select.select([self.rfile._sock], [], [], 0)[0]: 1225*cda5da8dSAndroid Build Coastguard Worker if not self.rfile._sock.recv(1): 1226*cda5da8dSAndroid Build Coastguard Worker break 1227*cda5da8dSAndroid Build Coastguard Worker stdout, stderr = p.communicate(data) 1228*cda5da8dSAndroid Build Coastguard Worker self.wfile.write(stdout) 1229*cda5da8dSAndroid Build Coastguard Worker if stderr: 1230*cda5da8dSAndroid Build Coastguard Worker self.log_error('%s', stderr) 1231*cda5da8dSAndroid Build Coastguard Worker p.stderr.close() 1232*cda5da8dSAndroid Build Coastguard Worker p.stdout.close() 1233*cda5da8dSAndroid Build Coastguard Worker status = p.returncode 1234*cda5da8dSAndroid Build Coastguard Worker if status: 1235*cda5da8dSAndroid Build Coastguard Worker self.log_error("CGI script exit status %#x", status) 1236*cda5da8dSAndroid Build Coastguard Worker else: 1237*cda5da8dSAndroid Build Coastguard Worker self.log_message("CGI script exited OK") 1238*cda5da8dSAndroid Build Coastguard Worker 1239*cda5da8dSAndroid Build Coastguard Worker 1240*cda5da8dSAndroid Build Coastguard Workerdef _get_best_family(*address): 1241*cda5da8dSAndroid Build Coastguard Worker infos = socket.getaddrinfo( 1242*cda5da8dSAndroid Build Coastguard Worker *address, 1243*cda5da8dSAndroid Build Coastguard Worker type=socket.SOCK_STREAM, 1244*cda5da8dSAndroid Build Coastguard Worker flags=socket.AI_PASSIVE, 1245*cda5da8dSAndroid Build Coastguard Worker ) 1246*cda5da8dSAndroid Build Coastguard Worker family, type, proto, canonname, sockaddr = next(iter(infos)) 1247*cda5da8dSAndroid Build Coastguard Worker return family, sockaddr 1248*cda5da8dSAndroid Build Coastguard Worker 1249*cda5da8dSAndroid Build Coastguard Worker 1250*cda5da8dSAndroid Build Coastguard Workerdef test(HandlerClass=BaseHTTPRequestHandler, 1251*cda5da8dSAndroid Build Coastguard Worker ServerClass=ThreadingHTTPServer, 1252*cda5da8dSAndroid Build Coastguard Worker protocol="HTTP/1.0", port=8000, bind=None): 1253*cda5da8dSAndroid Build Coastguard Worker """Test the HTTP request handler class. 1254*cda5da8dSAndroid Build Coastguard Worker 1255*cda5da8dSAndroid Build Coastguard Worker This runs an HTTP server on port 8000 (or the port argument). 1256*cda5da8dSAndroid Build Coastguard Worker 1257*cda5da8dSAndroid Build Coastguard Worker """ 1258*cda5da8dSAndroid Build Coastguard Worker ServerClass.address_family, addr = _get_best_family(bind, port) 1259*cda5da8dSAndroid Build Coastguard Worker HandlerClass.protocol_version = protocol 1260*cda5da8dSAndroid Build Coastguard Worker with ServerClass(addr, HandlerClass) as httpd: 1261*cda5da8dSAndroid Build Coastguard Worker host, port = httpd.socket.getsockname()[:2] 1262*cda5da8dSAndroid Build Coastguard Worker url_host = f'[{host}]' if ':' in host else host 1263*cda5da8dSAndroid Build Coastguard Worker print( 1264*cda5da8dSAndroid Build Coastguard Worker f"Serving HTTP on {host} port {port} " 1265*cda5da8dSAndroid Build Coastguard Worker f"(http://{url_host}:{port}/) ..." 1266*cda5da8dSAndroid Build Coastguard Worker ) 1267*cda5da8dSAndroid Build Coastguard Worker try: 1268*cda5da8dSAndroid Build Coastguard Worker httpd.serve_forever() 1269*cda5da8dSAndroid Build Coastguard Worker except KeyboardInterrupt: 1270*cda5da8dSAndroid Build Coastguard Worker print("\nKeyboard interrupt received, exiting.") 1271*cda5da8dSAndroid Build Coastguard Worker sys.exit(0) 1272*cda5da8dSAndroid Build Coastguard Worker 1273*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 1274*cda5da8dSAndroid Build Coastguard Worker import argparse 1275*cda5da8dSAndroid Build Coastguard Worker import contextlib 1276*cda5da8dSAndroid Build Coastguard Worker 1277*cda5da8dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 1278*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('--cgi', action='store_true', 1279*cda5da8dSAndroid Build Coastguard Worker help='run as CGI server') 1280*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-b', '--bind', metavar='ADDRESS', 1281*cda5da8dSAndroid Build Coastguard Worker help='bind to this address ' 1282*cda5da8dSAndroid Build Coastguard Worker '(default: all interfaces)') 1283*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-d', '--directory', default=os.getcwd(), 1284*cda5da8dSAndroid Build Coastguard Worker help='serve this directory ' 1285*cda5da8dSAndroid Build Coastguard Worker '(default: current directory)') 1286*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-p', '--protocol', metavar='VERSION', 1287*cda5da8dSAndroid Build Coastguard Worker default='HTTP/1.0', 1288*cda5da8dSAndroid Build Coastguard Worker help='conform to this HTTP version ' 1289*cda5da8dSAndroid Build Coastguard Worker '(default: %(default)s)') 1290*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('port', default=8000, type=int, nargs='?', 1291*cda5da8dSAndroid Build Coastguard Worker help='bind to this port ' 1292*cda5da8dSAndroid Build Coastguard Worker '(default: %(default)s)') 1293*cda5da8dSAndroid Build Coastguard Worker args = parser.parse_args() 1294*cda5da8dSAndroid Build Coastguard Worker if args.cgi: 1295*cda5da8dSAndroid Build Coastguard Worker handler_class = CGIHTTPRequestHandler 1296*cda5da8dSAndroid Build Coastguard Worker else: 1297*cda5da8dSAndroid Build Coastguard Worker handler_class = SimpleHTTPRequestHandler 1298*cda5da8dSAndroid Build Coastguard Worker 1299*cda5da8dSAndroid Build Coastguard Worker # ensure dual-stack is not disabled; ref #38907 1300*cda5da8dSAndroid Build Coastguard Worker class DualStackServer(ThreadingHTTPServer): 1301*cda5da8dSAndroid Build Coastguard Worker 1302*cda5da8dSAndroid Build Coastguard Worker def server_bind(self): 1303*cda5da8dSAndroid Build Coastguard Worker # suppress exception when protocol is IPv4 1304*cda5da8dSAndroid Build Coastguard Worker with contextlib.suppress(Exception): 1305*cda5da8dSAndroid Build Coastguard Worker self.socket.setsockopt( 1306*cda5da8dSAndroid Build Coastguard Worker socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) 1307*cda5da8dSAndroid Build Coastguard Worker return super().server_bind() 1308*cda5da8dSAndroid Build Coastguard Worker 1309*cda5da8dSAndroid Build Coastguard Worker def finish_request(self, request, client_address): 1310*cda5da8dSAndroid Build Coastguard Worker self.RequestHandlerClass(request, client_address, self, 1311*cda5da8dSAndroid Build Coastguard Worker directory=args.directory) 1312*cda5da8dSAndroid Build Coastguard Worker 1313*cda5da8dSAndroid Build Coastguard Worker test( 1314*cda5da8dSAndroid Build Coastguard Worker HandlerClass=handler_class, 1315*cda5da8dSAndroid Build Coastguard Worker ServerClass=DualStackServer, 1316*cda5da8dSAndroid Build Coastguard Worker port=args.port, 1317*cda5da8dSAndroid Build Coastguard Worker bind=args.bind, 1318*cda5da8dSAndroid Build Coastguard Worker protocol=args.protocol, 1319*cda5da8dSAndroid Build Coastguard Worker ) 1320