Still Failed to decode gzip: Failed to decompress data: gzip: invalid header

Hi So i have walked through the recommended approach but I am still getting this and I am very confused why.

# Uncomment this to pass the first stage
import gzip
import socket
from threading import Thread
import os
import sys


def response_body_builder(
    res_body: str, content_type: str = "text/plain", is_encoded: bool = False
):
    if is_encoded is False:

        return (
            "HTTP/1.1 200 OK\r\n"
            f"Content-Type: {content_type}\r\n"
            f"Content-Length: {len(res_body)}\r\n"
            "Connection: close\r\n"
            "\r\n"
            f"{res_body}"
            "\r\n"
        ).encode()

    res_body = compress_body(data=res_body)
    print(len(res_body))
    print(
        (
            "HTTP/1.1 200 OK\r\n"
            "Content-Encoding: gzip\r\n"
            f"Content-Type: {content_type}\r\n"
            f"Content-Length: {len(res_body)}\r\n\r\n"
            "Connection: close\r\n\r\n"
        ).encode()
        + res_body
    )
    return (
        "HTTP/1.1 200 OK\r\n"
        "Content-Encoding: gzip\r\n"
        f"Content-Type: {content_type}\r\n"
        f"Content-Length: {len(res_body)}\r\n\r\n"
        "Connection: close\r\n\r\n"
    ).encode() + res_body


def content_compression(data: str):

    headers, _ = data.split("\r\n\r\n", 1)

    allowed_compression = False
    for header in headers.splitlines():
        if header.lower().startswith("accept-encoding:"):
            compression = str(header.split(":")[1].strip())
            compression_list = compression.split(", ")
            if "gzip" in compression_list:
                allowed_compression = True

                break

    return allowed_compression


def body_builder(connection: socket.socket, data: str):
    headers, body = data.split("\r\n\r\n", 1)
    request_line = headers.splitlines()[0]
    print(f"Received request: {request_line}")

    # Process headers to find Content-Length
    content_length = 0
    for header in headers.splitlines():
        if header.lower().startswith("content-length:"):
            content_length = int(header.split(":")[1].strip())

    # Read the request body if Content-Length is specified
    if content_length > 0:
        while len(body) < content_length:
            body += connection.recv(1024).decode()
    return body


def compress_body(data: str):

    return gzip.compress(data=data.encode())


def main():
    # You can use print statements as follows for debugging, they'll be visible when running tests.
    print("Logs from your program will appear here!")

    # Uncomment this to pass the first stage

    server_socket = socket.create_server(("localhost", 4221), reuse_port=True)
    server_socket.listen()

    try:
        while True:
            connection, address = server_socket.accept()
            print(f"connected address: {address}")
            print(connection)
            Thread(target=socket_last_mile, args=(connection,)).start()
        # socket_last_mile(connection=connection)  # wait for client
    except (KeyboardInterrupt, Exception) as e:

        server_socket.close()


def socket_last_mile(connection: socket.socket):
    while True:
        data = connection.recv(1024).decode()
        if data is None:
            break
        resp_data = "HTTP/1.1 400 Bad Request\r\n\r\n"
        if len(data.split(" ")) < 2:

            break

        method, url_path = (
            data.split(" ")[0],
            data.split(" ")[1],
        )
        print(method, url_path)

        allowed_compression = content_compression(data=data)

        if url_path == "/":
            connection.send(b"HTTP/1.1 200 OK\r\n\r\n")
        elif "/echo/" in url_path:
            res_body = url_path.split("/")[-1]

            resp_data = response_body_builder(
                res_body=res_body.strip(), is_encoded=allowed_compression
            )

        elif "/files/" in url_path:
            file_path = os.path.join(sys.argv[2], url_path.split("/")[-1])

            if method == "GET":

                if os.path.exists(file_path):
                    with open(file=file_path) as f:
                        body = f.read()

                        resp_data = response_body_builder(
                            res_body=body,
                            content_type="application/octet-stream",
                            is_encoded=allowed_compression,
                        )
                else:
                    resp_data = "HTTP/1.1 404 Not Found\r\n\r\n"
            if method == "POST":
                file = open(file=file_path, mode="w+")
                file.write(body_builder(connection=connection, data=data))
                file.close()

                resp_data = "HTTP/1.1 201 Created\r\n\r\n"

            ...

        elif url_path == "/user-agent":

            user_agent = data.split(" ")[-1]

            resp_data = response_body_builder(
                res_body=user_agent.strip(), is_encoded=allowed_compression
            )

        else:
            resp_data = "HTTP/1.1 404 Not Found\r\n\r\n"

        connection.sendall(resp_data)
    connection.close()


if __name__ == "__main__":
    main()

Returns bytes which does not represent a valid string.

okay.

So does that means that i have to convert from bytes to string?

Actually I take that back, “abc”.encode() + bytes([1,2,3]) does seem to produce bytes in python. You should enable debug mode and add the output here.

Here is the response for the server.

 curl -v -H "Accept-Encoding: gzip" http://localhost:4221/echo/pineapple
*   Trying 127.0.0.1:4221...
* Connected to localhost (127.0.0.1) port 4221 (#0)
> GET /echo/pineapple HTTP/1.1
> Host: localhost:4221
> User-Agent: curl/8.1.2
> Accept: */*
> Accept-Encoding: gzip
> 
< HTTP/1.1 200 OK
< Content-Encoding: gzip
< Content-Type: text/plain
< Content-Length: 29
< 
* Excess found in a read: excess = 81, size = 29, maxdownload = 29, bytecount = 0
Connection: close

* Closing connection 0
b'\x1f\x%                                              

The solution is to encode the base response and add with the zipped/ zip encoded data.

CURL will not show the response in the terminal but it should work.