5

I need to build a http server without using an HTTP library.

I have the server running and an html page beeing loaded but my <img src="..."/> tags are not beeing loaded, I recive the call but cannot preset the png/JPEG in the page.

httpServer.py

# Define socket host and port
SERVER_HOST = '0.0.0.0'
SERVER_PORT = 8000

# Create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((SERVER_HOST, SERVER_PORT))
server_socket.listen(1)
print('Listening on port %s ...' % SERVER_PORT)

while True:
    # Wait for client connections
    client_connection, client_address = server_socket.accept()

    # Handle client request
    request = client_connection.recv(1024).decode()
    content = handle_request(request)

    # Send HTTP response
    if content:
        response = 'HTTP/1.1 200 OK\n\n'
        response += content
    else:
        response = 'HTTP/1.1 404 NOT FOUND\n\nFile Not Found'

    client_connection.sendall(response.encode())
    client_connection.close()

# Close socket
server_socket.close()

Function where handles the call

 def handle_request(request):
    http = HttpHandler.HTTPHandler

    # Parse headers
    print(request)
    headers = request.split('\n')
    get_content = headers[0].split()

    accept = headers[6].split()
    type_content = accept[1].split('/')

    try:
        # Filename
        filename = get_content[1]

        if get_content[0] == "GET":
            content = http.get(None, get_content[1], type_content[0])


        return content
    except FileNotFoundError:
        return None

class to handle the http verbs

class HTTPHandler:

    def get(self, args, type):
        if args == '/':
            args = '/index.html'
            fin = open('htdocs' + args)
        if type != "image":
            fin = open('htdocs/' + args)

        if type == "image":
            fin = open('htdocs/' + args, 'rb')

        # Read file contents
        content = fin.read()
        fin.close()
        return content

Realize that I´m trying to make an HTTP 1.1, if you see anything out of pattern fell free to say thanks in advance.

vladimir
  • 13,428
  • 2
  • 44
  • 70
Filipe
  • 170
  • 1
  • 1
  • 11
  • Why can't you use [`http.server`](https://docs.python.org/3/library/http.server.html). Are you doing it this way as a learning exercise? If so, maybe you could get a few ideas by looking at the `http.server` module's source code, or the Python 2 [`BaseHTTPServer`](https://stackoverflow.com/a/50407236/4014959). – PM 2Ring May 20 '18 at 01:59
  • Maybe I'm going blind, but I don't see where that code sends the header lines to the browser. – PM 2Ring May 20 '18 at 02:00
  • @PM2Ring yeah , i'm doing this as a learning exercise, and the link you send, i can return a string, with no problem with this code, when the page is loaded, if i have a `` tag the browser makes another call for the resource, thats where the problem appears, I don't know how to send the image – Filipe May 20 '18 at 02:16
  • In response to the browser's request you need to send the correct headers as well as the binary image data. [This answer](https://stackoverflow.com/a/14728303/4014959) shows what the headers look like. – PM 2Ring May 20 '18 at 03:10

2 Answers2

3

I don't know where you've learnt how HTTP works but I'm pretty sure that you did not study the actual standard which you should do when implementing a protocol. Some notes about your implementation:

  • Line ends should be \r\n not \n. This is true for both responses from the server as requests from the client.
  • You are assuming that the clients requests is never larger than 1024 bytes and that it can be read within a single recv. But, requests can have arbitrary length and there is no guarantee that you get all within a single recv (TCP is a streaming protocol and not a message protocol).
  • While it is kind of ok to simply close the TCP connection after the body it would be better to include the length of the body in the Content-length header or use chunked transfer encoding.
  • The type of the content should be given by using the Content-Type header, i.e. Content-type: text/html for HTML and Content-type: image/jpeg for JPEG images. Without this browser might guess correctly or wrongly what the type might be or depending on the context might also insist on a proper content-type header.

Apart from that, if you debug such problems it is helpful to find out what gets actually exchanged between client and server. It might be that you've checked this for yourself but you did not include such information into your question. Your only error description is "...I recive the call but cannot preset the png/JPEG in the page" and then a dump of your code.

Community
  • 1
  • 1
Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Thanks for the help with the standard, the problem was that I was sending the bytes as a string (in the concatenation), and not as bytes. – Filipe May 20 '18 at 15:04
2

httpServer.py

Ended up like:

while True:
# Wait for client connections
client_connection, client_address = server_socket.accept()

# Handle client request
request = client_connection.recv(10240).decode()
content = handle_request(request)

# Send HTTP response
if content:
    if str(content).find("html") > 0:
        client_connection.send('HTTP/1.1 200 OK\n\n'.encode())
        client_connection.send(content.encode())
    else:
        client_connection.send('HTTP/1.1 200 OK\r\n'.encode())
        client_connection.send("Content-Type: image/jpeg\r\n".encode())
        client_connection.send("Accept-Ranges: bytes\r\n\r\n".encode())
        client_connection.send(content)
else:
    response = 'HTTP/1.1 404 NOT FOUND\r\nFile Not Found'

client_connection.close()

And the Get method like:

class HTTPHandler:

def get(self, args, type):
    if args == '/':
        args = '/index.html'
        fin = open('htdocs' + args)
    if type != "image":
        fin = open('htdocs/' + args)

    if type.find("html") == -1:
        image_data = open('htdocs/' + args, 'rb')
        bytes = image_data.read()

        # Content-Type: image/jpeg, image/png \n\n
        content = bytes
        fin.close()
        return content

    # Read file contents
    content = fin.read()
    fin.close()
    return content
Filipe
  • 170
  • 1
  • 1
  • 11