Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly send HTTP response with Python using socket library only?

I have a very simple web sever written in Python. It listens on port 13000, how can I make it deliver a simple "Hello World" webpage if http://localhost:13000 is opened in browser?

Right there is my code:

# set up socket and connection
while True:
    sock, addr = servSock.accept()
    # WHAT GOES HERE?
    sock.close()

As you can see, I am not exactly sure how to actually send back the webpage?

I only have to use the socket library.

EDIT: The problem is not that I don't know how to formulate the HTTP response, I don't know how to actually make it display in my browser! It just keeps spinning/loading.

like image 781
antonpug Avatar asked Apr 11 '12 21:04

antonpug


People also ask

How does Python connect with HTTP socket?

This is very simple to create a socket client using Python's socket module function. The socket. connect(hosname, port ) opens a TCP connection to hostname on the port. Once you have a socket open, you can read from it like any IO object.

Can socket use HTTP?

The socket API supports different protocols from the transport layer and down. That means that if you would like to use TCP you use sockets. But you can also use sockets to communicate using HTTP, but then you have to decode/encode messages according to the HTTP specification (RFC2616).


Video Answer


2 Answers

Updated according to question change

Possibly, it keeps spinning because in combination of absense of Content-Length and Connection headers, browser may assume it's Connection: keep-alive, so it continues to receive data from your server forever. Try to send Connection: close, and pass actual Content-Length to see if that helps.


Won't this do what you expect it to? :)
#!/usr/bin/env python
# coding: utf8

import socket

MAX_PACKET = 32768

def recv_all(sock):
    r'''Receive everything from `sock`, until timeout occurs, meaning sender
    is exhausted, return result as string.'''

    # dirty hack to simplify this stuff - you should really use zero timeout,
    # deal with async socket and implement finite automata to handle incoming data

    prev_timeout = sock.gettimeout()
    try:
        sock.settimeout(0.01)

        rdata = []
        while True:
            try:
                rdata.append(sock.recv(MAX_PACKET))
            except socket.timeout:
                return ''.join(rdata)

        # unreachable
    finally:
        sock.settimeout(prev_timeout)

def normalize_line_endings(s):
    r'''Convert string containing various line endings like \n, \r or \r\n,
    to uniform \n.'''

    return ''.join((line + '\n') for line in s.splitlines())

def run():
    r'''Main loop'''

    # Create TCP socket listening on 10000 port for all connections, 
    # with connection queue of length 1
    server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, \
                                socket.IPPROTO_TCP)
    server_sock.bind(('0.0.0.0', 13000))
    server_sock.listen(1)

    while True:
        # accept connection
        client_sock, client_addr = server_sock.accept()

        # headers and body are divided with \n\n (or \r\n\r\n - that's why we
        # normalize endings). In real application usage, you should handle 
        # all variations of line endings not to screw request body
        request = normalize_line_endings(recv_all(client_sock)) # hack again
        request_head, request_body = request.split('\n\n', 1)

        # first line is request headline, and others are headers
        request_head = request_head.splitlines()
        request_headline = request_head[0]
        # headers have their name up to first ': '. In real world uses, they
        # could duplicate, and dict drops duplicates by default, so
        # be aware of this.
        request_headers = dict(x.split(': ', 1) for x in request_head[1:])

        # headline has form of "POST /can/i/haz/requests HTTP/1.0"
        request_method, request_uri, request_proto = request_headline.split(' ', 3)

        response_body = [
            '<html><body><h1>Hello, world!</h1>',
            '<p>This page is in location %(request_uri)r, was requested ' % locals(),
            'using %(request_method)r, and with %(request_proto)r.</p>' % locals(),
            '<p>Request body is %(request_body)r</p>' % locals(),
            '<p>Actual set of headers received:</p>',
            '<ul>',
        ]

        for request_header_name, request_header_value in request_headers.iteritems():
            response_body.append('<li><b>%r</b> == %r</li>' % (request_header_name, \
                                                    request_header_value))

        response_body.append('</ul></body></html>')

        response_body_raw = ''.join(response_body)

        # Clearly state that connection will be closed after this response,
        # and specify length of response body
        response_headers = {
            'Content-Type': 'text/html; encoding=utf8',
            'Content-Length': len(response_body_raw),
            'Connection': 'close',
        }

        response_headers_raw = ''.join('%s: %s\n' % (k, v) for k, v in \
                                                response_headers.iteritems())

        # Reply as HTTP/1.1 server, saying "HTTP OK" (code 200).
        response_proto = 'HTTP/1.1'
        response_status = '200'
        response_status_text = 'OK' # this can be random

        # sending all this stuff
        client_sock.send('%s %s %s' % (response_proto, response_status, \
                                                        response_status_text))
        client_sock.send(response_headers_raw)
        client_sock.send('\n') # to separate headers from body
        client_sock.send(response_body_raw)

        # and closing connection, as we stated before
        client_sock.close()

run()

For more detailed description, please see description of HTTP protocol.

like image 53
toriningen Avatar answered Oct 02 '22 17:10

toriningen


# set up socket and connection
while True:
    sock, addr = servSock.accept()
    sock.send("""HTTP/1.1 200 OK
Content-Type: text/html

<html><body>Hello World</body></html>
""");
    sock.close()
like image 20
Luky Avatar answered Oct 02 '22 17:10

Luky