-
-
Save bradmontgomery/2219997 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
""" | |
Very simple HTTP server in python (Updated for Python 3.7) | |
Usage: | |
./dummy-web-server.py -h | |
./dummy-web-server.py -l localhost -p 8000 | |
Send a GET request: | |
curl http://localhost:8000 | |
Send a HEAD request: | |
curl -I http://localhost:8000 | |
Send a POST request: | |
curl -d "foo=bar&bin=baz" http://localhost:8000 | |
This code is available for use under the MIT license. | |
---- | |
Copyright 2021 Brad Montgomery | |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and | |
associated documentation files (the "Software"), to deal in the Software without restriction, | |
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, | |
subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all copies or substantial | |
portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT | |
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
""" | |
import argparse | |
from http.server import HTTPServer, BaseHTTPRequestHandler | |
class S(BaseHTTPRequestHandler): | |
def _set_headers(self): | |
self.send_response(200) | |
self.send_header("Content-type", "text/html") | |
self.end_headers() | |
def _html(self, message): | |
"""This just generates an HTML document that includes `message` | |
in the body. Override, or re-write this do do more interesting stuff. | |
""" | |
content = f"<html><body><h1>{message}</h1></body></html>" | |
return content.encode("utf8") # NOTE: must return a bytes object! | |
def do_GET(self): | |
self._set_headers() | |
self.wfile.write(self._html("hi!")) | |
def do_HEAD(self): | |
self._set_headers() | |
def do_POST(self): | |
# Doesn't do anything with posted data | |
self._set_headers() | |
self.wfile.write(self._html("POST!")) | |
def run(server_class=HTTPServer, handler_class=S, addr="localhost", port=8000): | |
server_address = (addr, port) | |
httpd = server_class(server_address, handler_class) | |
print(f"Starting httpd server on {addr}:{port}") | |
httpd.serve_forever() | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="Run a simple HTTP server") | |
parser.add_argument( | |
"-l", | |
"--listen", | |
default="localhost", | |
help="Specify the IP address on which the server listens", | |
) | |
parser.add_argument( | |
"-p", | |
"--port", | |
type=int, | |
default=8000, | |
help="Specify the port on which the server listens", | |
) | |
args = parser.parse_args() | |
run(addr=args.listen, port=args.port) |
Thanks!
Brad, thanks you!
Also I am using Python 3.7 and the following corrections were needed for provided snippet to work properly:
- In imports:
from http.server import BaseHTTPRequestHandler, HTTPServer
import socketserver
- Encode string arguments of
self.wfile.write
like this:
self.wfile.write("<html><body><h1>hi!</h1></body></html>".encode("utf-8"))
self.wfile.write("<html><body><h1>POST!</h1></body></html>".encode("utf-8"))
For testing POST, if encountering
"ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host"
Need to use curl as below:
curl -d "foo=bar&bin=baz" http://localhost --tlsv1.2
No
import SocketServer
is not needed and can be removed.
It works for me without this import with python 2.7...
I take it's python 2.7, as print function doesn't use square brackets and:
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
Traceback (most recent call last):
File "", line 1, in
Any chance thsi wil lbe made in version 3?
also, please mark code as 2.* version of python somewhere in title.
Used this as a base for a static JSON File Server in Python 3.7. Files are a in a sub directory called cached-responses
and they all have .json
extension.
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import os
base_path = os.path.dirname(__file__)
class StaticServer(BaseHTTPRequestHandler):
def execute_request(self):
filename = 'cached-responses' + self.path + '.json'
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
with open(os.path.join(base_path, filename), 'rb') as fh:
self.wfile.write(fh.read())
def do_POST(self):
self.execute_request()
def do_GET(self):
self.execute_request()
def run(server_class=HTTPServer, handler_class=StaticServer, port=8000):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print('Starting Server on port {}'.format(port))
httpd.serve_forever()
run()
😬 I've somehow missed all the activity on this gist all these years! A very late thank you to everyone that's commented.
I've updated the original post for Python 3.7, and cleaned it up a bit. Hope this continues to be useful 🙇♂️
Good stuff!
Wow, neat. It even takes CLI arguments!
@joshwlewis @emccrckn 🤣 👍 💯
good start!
Just tumbled into this myself. ! fantastic.
For python 3.7 on windows 10, I had to add decode to the post_data.
def do_POST(self):
content_length = int(self.headers['Content-Length']) # <--- Gets the size of data
post_data = self.rfile.read(content_length) # <--- Gets the data itself
pd = post_data.decode("utf-8") # <-------- ADD this line
self._set_headers()
self.wfile.write(self._html("POST! "+pd))
I need help, for the 'Content-Length' its always 0, is there something i'm doing wrong?
Looks great! only missing multi-thread/daemon feature ;)
Great Simple Little Utility. For those asking about extra features like response codes etc. This is a VERY SIMPLE Server. It does as what's advertised.
This probably still needs a 404 somewhere to show how to send proper http status numbers.
thank you.