8

I need to implement a very simple web-server-like app in Python which would perform basic HTTP requests and responses and display very basic output on the web page. I am not too concerned about actually coding it in Python, but I am not sure where to start? How to set this up? One file? Multiple files? I guess I have no idea how to approach the fact that this is a "server" - so I am unfamiliar with how to approach dealing with HTTP requests/sockets/processing requests, etc. Any advice? Resources?

antonpug
  • 13,724
  • 28
  • 88
  • 129
  • Explain further your requirements (and why). Are you wanting to implement the full application-level stack, or can you use e.g. Apache? Explain more what you need *And why.* Finally, when you have the "websocket" tag, do you mean it or was that just an incorrect tag to use? – Chris Morgan Apr 10 '12 at 15:08
  • 2
    Without using *any* libraries? Surely `socket` is going to be required at a minimum? – Wooble Apr 10 '12 at 15:09
  • check [this documentation](http://docs.python.org/library/simplehttpserver.html) page, here I assume that "any libraries" are libraries that are not included in the python installation. – Emil M Apr 10 '12 at 15:10
  • @Wooble: I think it is quite fair to assume "any libraries" to mean "any non-standard libraries". – Chris Morgan Apr 10 '12 at 15:12

4 Answers4

10

You should look at the SimpleHttpServer (py3: http.server) module.

Depending on what you're trying to do, you can either just use it, or check out the module's source (py2, py3) for ideas.

If you want to get more low-level, SimpleHttpServer extends BaseHttpServer (source) to make it just work.

If you want to get even more low-level, take a look at SocketServer (source: py2, py3).

People will often run python like python -m SimpleHttpServer (or python3 -m http.server) if they just want to share a directory: it's a fully functional and... simple server.

depperm
  • 10,606
  • 4
  • 43
  • 67
quodlibetor
  • 8,185
  • 4
  • 35
  • 48
6

You can use socket programming for this purpose. The following snippet creates a tcp socket and listens on port 9000 for http requests:

from socket import *

def createServer():
    serversocket = socket(AF_INET, SOCK_STREAM)
    serversocket.bind(('localhost',9000))
    serversocket.listen(5)
    while(1):
        (clientsocket, address) = serversocket.accept()
        clientsocket.send("HTTP/1.1 200 OK\n"
         +"Content-Type: text/html\n"
         +"\n" # Important!
         +"<html><body>Hello World</body></html>\n")
        clientsocket.shutdown(SHUT_WR)
        clientsocket.close()

    serversocket.close()

createServer()

Start the server, $ python server.py. Open http://localhost:9000/ in your web-browser (which acts as client). Then in the browser window, you can see the text "Hello World" (http response).

EDIT** The previous code was only tested on chrome, and as you guys suggested about other browsers, the code was modified as:

  1. To make the response http-alike you can send in plain header with http version 1.1, status code 200 OK and content-type text/html.
  2. The client socket needs to be closed once response is submitted as it's a TCP socket.
  3. To properly close the client socket, shutdown() needs to be called socket.shutdown vs socket.close

Then the code was tested on chrome, firefox (http://localhost:9000/) and simple curl in terminal (curl http://localhost:9000).

Xantium
  • 11,201
  • 10
  • 62
  • 89
pritam
  • 2,452
  • 1
  • 21
  • 31
  • 4
    -1. Did you try this? This doesn't work at all; it's a socket server, but doesn't speak HTTP. – Asherah May 18 '12 at 02:57
  • 3
    It doesn't actually speak HTTP, so almost every browser wouldn't work with this. It doesn't work in Chrome. It only works in Firefox if you press the Stop button. This is not a web server. – Asherah May 18 '12 at 03:02
  • I copied your script ran it and I'm getting an error saying `a byte-like object is required not 'str'` how do I fix this? – Xantium Oct 07 '17 at 10:40
  • 3
    @Simon Modified the code a bit. Also it was tested on python 2.7.6. If you are using python 3, where strings are Unicode; but when transmitting on the network, we need to send bytes strings instead. So try sendall() instead of send() with string output encoded as utf-8. #START output = "It works" clientsocket.sendall(output.encode("utf-8")) #END – pritam Oct 10 '17 at 09:59
  • Super! That's just what I needed. I do use Python 3 so the ` sendall()` and `.encode("utf-8")` are absolutely essential and both these have fixed the error I was getting. all my server attempts up to this point have failed so I am very grateful. This is a well deserved up-vote however I do not like the `clientsocket.send("HTTP/1.1 200 OK\n" +"Content-Type: text/html\n" +"\n" # Important! +"Hello World\n")` shouldn't that be all on the same line? I had to modify that part. Thanks again. : ) – Xantium Oct 10 '17 at 17:18
  • @Ashe It works normally in Chrome/Firefox/Edge for me. As I mentioned the byte-like error but apart from that this works great. – Xantium Mar 17 '18 at 12:43
  • @Simon the answer as originally posted (*six years ago*) did not have anything resembling HTTP; see https://stackoverflow.com/revisions/10592712/1. – Asherah Mar 18 '18 at 03:49
2

I decided to make this work in Python 3 and make it work for Chrome to use as an example for an online course I am developing. Python 3 of course needs encode() and decode() in the right places. Chrome - really wants to send its GET request before it gets data. I also added some error checking so it cleans up its socket if you abort the server or it blows up:

def createServer():
    serversocket = socket(AF_INET, SOCK_STREAM)
    try :
        serversocket.bind(('localhost',9000))
        serversocket.listen(5)
        while(1):
            (clientsocket, address) = serversocket.accept()

            rd = clientsocket.recv(5000).decode()
            pieces = rd.split("\n")
            if ( len(pieces) > 0 ) : print(pieces[0])

            data = "HTTP/1.1 200 OK\r\n"
            data += "Content-Type: text/html; charset=utf-8\r\n"
            data += "\r\n"
            data += "<html><body>Hello World</body></html>\r\n\r\n"
            clientsocket.sendall(data.encode())
            clientsocket.shutdown(SHUT_WR)

    except KeyboardInterrupt :
        print("\nShutting down...\n");
    except Exception as exc :
        print("Error:\n");
        print(exc)

    serversocket.close()

print('Access http://localhost:9000')
createServer()

The server also prints out the incoming HTTP request. The code of course only sends text/html regardless of the request - even if the browser is asking for the favicon:

$ python3 server.py
Access http://localhost:9000
GET / HTTP/1.1
GET /favicon.ico HTTP/1.1
^C
Shutting down...

But it is a pretty good example that mostly shows why you want to use a framework like Flask or DJango instead of writing your own. Thanks for the initial code.

drchuck
  • 4,415
  • 3
  • 27
  • 30
-1

There is a very simple solution mentioned above, but the solution above doesn't work. This solution is tested on chrome and it works. This is python 3 although it may work on python 2 since I never tested it.

from socket import *

def createServer():
    serversocket = socket(AF_INET, SOCK_STREAM)
    serversocket.bind(('localhost',9000))
    serversocket.listen(5)
    while(1):
        (clientsocket, address) = serversocket.accept()
        clientsocket.send(bytes("HTTP/1.1 200 OK\n"
         +"Content-Type: text/html\n"
         +"\n" # Important!
         +"<html><body>Hello World</body></html>\n",'utf-8'))
        clientsocket.shutdown(SHUT_WR)
        clientsocket.close()

    serversocket.close()

createServer()

This is improved from the answer that was accepted, but I will post this so future users can use it easily.

PythonPro
  • 291
  • 2
  • 11
  • Welcome to Stack Overflow. This question is a near duplicate of the [answer provided by @drchuck](https://stackoverflow.com/a/53157058/148680). The only thing that differentiates it from the latter is the lack of exception handling. – chb Jan 26 '19 at 20:53