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))
Exemple #2
0
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