def runOnce(sock: socket.socket) -> None: (connection, (host, port)) = sock.accept() header = bytearray() while not header.endswith(b"\r\n" * 2): chunk = connection.recv(1) header.extend(chunk) request = Request.fromBytes(header) # parse request if "Content-Length" in request.headers: while len(request.body) < int(request.headers["Content-Length"]): chunk = connection.recv(4096) request.body.extend(chunk) else: # if browser does not include a Content-Length header, then request has no body pass # while True: # chunk: bytes = connection.recv(4096) # if chunk: # request.body.extend(chunk) # else: # break headers = {"Content-Type": "application/json"} if request.pathname != "/product": response = Response(404, body=b"404 Not Found") elif request.params == {}: response = Response(400, body=b"400 Bad Request") else: try: operands = list(map(float, request.params.values())) # this could fail result = reduce(lambda x, y: x * y, operands) body = { "operation": "product", "operands": operands, "result": result } response = Response(200, headers=headers, body=bytes(json.dumps(body, indent=4), "utf8")) except: response = Response(400, body=b"400 Bad Request") connection.sendall(bytes(response)) connection.close() print("{} {} {}".format(request.method, request.url, response.statusCode))
def runForever(port): sock = socket.socket( family=socket.AF_INET, type=socket.SOCK_STREAM ) # according to <https://docs.python.org/3/library/socket.html#socket.AF_INET> sock.bind(("", port)) sock.listen(5) while True: try: (connection, (host, port)) = sock.accept() except: print("Keyboard interrupt. Exitting.") sock.close() break header = bytearray() while not header.endswith(b"\r\n" * 2): # read the header only chunk = connection.recv(1) header.extend(chunk) request = Request.fromBytes(header) # parse request path = "." + request.pathname headers = { "Content-Type": "text/html", } if os.path.exists(path): if path.endswith(".html") or path.endswith(".htm"): response = Response(200, headers=headers) with open(os.path.join(".", path), "rb") as f: response.body.extend(f.read()) else: response = Response(403, body=b"<h1>403 Forbidden</h1>", headers=headers) else: response = Response(404, body=b"<h1>404 Not Found</h1>", headers=headers) connection.sendall(bytes(response)) print("{} {} {}".format(request.method, request.pathname, response.statusCode)) connection.close()
def runForever(port): sock = socket.socket( family=socket.AF_INET, type=socket.SOCK_STREAM ) # according to <https://docs.python.org/3/library/socket.html#socket.AF_INET> sock.bind(("", port)) sock.listen(5) readers = { sock: None, } while True: try: readables, *_ = select.select(readers, [], []) except: print("Keyboard interrupt. Exitting.") for v in readers.keys(): # clean up v.close() break for readable in readables: if readable is sock: # new connection coming in (connection, (ip, port)) = sock.accept() readers[connection] = { "state": "header", "header": bytearray() } else: # other clients if readers[readable][ "state"] == "header": # in the state of reading header chunk = readable.recv(1) readers[readable]["header"].extend(chunk) if readers[readable]["header"].endswith( b"\r\n" * 2): # request header fully transferred try: request = Request.fromBytes( readers[readable] ["header"]) # parse request header except: # fail to parse header traceback.print_exc() response = Response( 403, body=b"HTTP request is invalid: <pre>" + readers[readable]["header"] + b"</pre>") readable.sendall(bytes(response)) readable.close() print("{} {} {}".format(request.method, request.pathname, response.statusCode)) readers.pop(readable) continue if ( "Content-Length" in request.headers and request.headers["Content-Length"] == 0 ) or "Content-Length" not in request.headers: # if Content-Length: 0 or Content-Length not available, serve immediately response = staticFile( request.pathname) # generate response readable.sendall(bytes(response)) # serve response readable.close() print("{} {} {}".format(request.method, request.pathname, response.statusCode)) del readers[readable] else: # need to read the whole request body readers[readable]["state"] = "body" readers[readable]["request"] = request readers[readable].pop("header") continue else: # request header not fully transferred continue # keep reading in the next iteration else: # in the state of reading body chunk = readable.recv(4096) request = readers[readable]["request"] request.body.extend(chunk) if len(request.body) >= int( request.headers["Content-Length"] ): # there is a Content-Length, guaranteed, because we have served all requests that do not have one already response = staticFile(request.pathname) readable.sendall(bytes(response)) readable.close() print("{} {} {}".format(request.method, request.pathname, response.statusCode)) readers.pop(readable) else: continue