this is my Python3 project hiearchy:
projet
\
script.py
web
\
index.html
From script.py, I would like to run a http server which serve the content of the web folder.
Here is suggested this code to run a simple http server:
import http.server
import socketserver
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", PORT), Handler)
print("serving at port", PORT)
httpd.serve_forever()
but this actually serve project, not web. How can I specify the path of the folder I want to serve?
In Python 3.7 SimpleHTTPRequestHandler can take a directory argument:
import http.server
import socketserver
PORT = 8000
DIRECTORY = "web"
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs)
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
and from the command line:
python -m http.server --directory web
To get a little crazy... you could make handlers for arbitrary directories:
def handler_from(directory):
def _init(self, *args, **kwargs):
return http.server.SimpleHTTPRequestHandler.__init__(self, *args, directory=self.directory, **kwargs)
return type(f'HandlerFrom<{directory}>',
(http.server.SimpleHTTPRequestHandler,),
{'__init__': _init, 'directory': directory})
with socketserver.TCPServer(("", PORT), handler_from("web")) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
If you just want serve static file you can do it by running SimpleHTTPServer module using python 2:
python -m SimpleHTTPServer
Or with python 3:
python3 -m http.server
This way you do not need to write any script.
https://docs.python.org/3/library/http.server.html#http.server.SimpleHTTPRequestHandler
This class serves files from the current directory and below, directly
mapping the directory structure to HTTP requests.
So you just need to change the current directory prior to starting the server - see os.chdir
eg:
import http.server
import socketserver
import os
PORT = 8000
web_dir = os.path.join(os.path.dirname(__file__), 'web')
os.chdir(web_dir)
Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", PORT), Handler)
print("serving at port", PORT)
httpd.serve_forever()
Just for completeness, here's how you can setup the actual server classes to serve files from an arbitrary directory:
try
# python 2
from SimpleHTTPServer import SimpleHTTPRequestHandler
from BaseHTTPServer import HTTPServer as BaseHTTPServer
except ImportError:
# python 3
from http.server import HTTPServer as BaseHTTPServer, SimpleHTTPRequestHandler
class HTTPHandler(SimpleHTTPRequestHandler):
"""This handler uses server.base_path instead of always using os.getcwd()"""
def translate_path(self, path):
path = SimpleHTTPRequestHandler.translate_path(self, path)
relpath = os.path.relpath(path, os.getcwd())
fullpath = os.path.join(self.server.base_path, relpath)
return fullpath
class HTTPServer(BaseHTTPServer):
"""The main server, you pass in base_path which is the path you want to serve requests from"""
def __init__(self, base_path, server_address, RequestHandlerClass=HTTPHandler):
self.base_path = base_path
BaseHTTPServer.__init__(self, server_address, RequestHandlerClass)
Then you can set any arbitrary path in your code:
web_dir = os.path.join(os.path.dirname(__file__), 'web')
httpd = HTTPServer(web_dir, ("", 8000))
httpd.serve_forever()
There's a shorter method for Python 3+:
import functools
Handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory='/my/dir/goes/here')
You also can run the command line with
python3 -m http.server -d web 8000
Another easy method to serve from a specific directory.
Since you really only need to set the directory parameter for the SimpleHTTPRequestHandler, you can use functools.partial to prepare the handler class without instantiating the class.
from functools import partial
from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
def start_httpd(directory: Path, port: int = 8000):
print(f"serving from {directory}...")
handler = partial(SimpleHTTPRequestHandler, directory=directory)
httpd = HTTPServer(('localhost', port), handler)
httpd.serve_forever()
If you just need a modern web static server,
deno is an alternative file server without any code.
install deno with single line
https://github.com/denoland/deno_install#deno_install
install file server with single line
deno install --allow-net --allow-read https://deno.land/std#0.125.0/http/file_server.ts
use deno file-server
file_server . --port=<port>
# Downloading https://deno.land/std#0.125.0/http/file_server.ts...
# HTTP server listening on http://0.0.0.0:<port>/
read more https://deno.land/manual/examples/file_server#using-the-codestdhttpcode-file-server
Related
This is interesting. I did a simple script to bind and serve http but I hadn't done this in Python3. I can write a simple server:
import http.server
import socketserver
PORT = 8002
Handler = http.server.SimpleHTTPRequestHandler
#https://docs.python.org/3/library/http.server.html
class MyHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, request, client_addr, server):
super().__init__(request, client_addr, server)
def do_GET(self, ):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.wfile.write('Hey!'.encode())
httpd = socketserver.TCPServer(("0.0.0.0", PORT), MyHandler)
print("serving at port", PORT)
httpd.serve_forever()
but when I run it, then Ctrl+c, then run it again it says:
OSError: [Errno 98] Address already in use
Why is that if I kill the previous process?
Also, is there any reason other than that that this couldn't be used as a simple, testing webapp for a test server at IP:port/somesamplewebapp - They say "http.server is not recommended for production. It only implements basic security checks." but if it does not need https or extra security... what are the risks?
The operating system prevents, by default, the reuse of an address by a different PID. You can defeat that with the socket option SO_REUSEADDR. However, since you are using the TCPServer class and it has it's own, different, way of specifying that. You can use this code.
import http.server
import socketserver
PORT = 8002
Handler = http.server.SimpleHTTPRequestHandler
#https://docs.python.org/3/library/http.server.html
class MyHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, request, client_addr, server):
super().__init__(request, client_addr, server)
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.wfile.write('Hey!'.encode())
class MyServer(socketserver.TCPServer):
allow_reuse_address = True # <-- This is what you need
httpd = MyServer(("0.0.0.0", PORT), MyHandler)
print("serving at port", PORT)
httpd.serve_forever()
I coded following simple http server in python, I have some files without an extension and want to serve them with "text/plain" mime type. How can I achieve it?
import SimpleHTTPServer
import SocketServer
PORT = 80
class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
pass
Handler.extensions_map['.shtml'] = 'text/html'
httpd = SocketServer.TCPServer(("", PORT), Handler)
print "serving at port", PORT
httpd.serve_forever()
According to the module's source code this should work:
Handler.extensions_map[''] = 'text/plain'
my python codes
# _*_ coding:utf-8 _*_
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
import SocketServer
class testHTTPSERVER_RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
self.wfile.write("<html><body><h1>Hello world</h1></body></html>")
def run(server_class=HTTPServer,handler_class=BaseHTTPRequestHandler):
print('starting server ....')
server_address = ('127.0.0.1',8081)
httpd = server_class(server_address,testHTTPSERVER_RequestHandler)
print('running server')
httpd.serve_forever()
run()
and docker file
FROM python:2.7-onbuild
#python:2.7.13-alpine dene
ADD testhttp_server.py /src/app/testhttp_server.py
WORKDIR /src/app/
EXPOSE 8081
CMD ["python","testhttp_server.py"]
docker run and logs
mozilla
so where is my mistake??I'm working on that during two days but I didn't find anything else
Change this
server_address = ('127.0.0.1',8081)
to
server_address = ('0.0.0.0',8081)
Listening to 127.0.0.1 inside docker container means you want to listen to traffic generated from inside the container only but when you map a host to container. It is sending the traffic from host to the IP of the container. Which id dynamic and not know before hand. So you need to listen to all interfaces inside the container. And that is why you should use 0.0.0.0
I want to launch Python HTTPServer on heroku. Note that this is no Python framework. The code snippet is attached below. How will I be able to launch this server on Heroku? I am able to run this server on my local machine. But I want it deployed on Heroku. Please provide insights.
Server Code:
import http.server
from http.server import HTTPServer, BaseHTTPRequestHandler
import socketserver
import threading
PORT = 5001
class myHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.write("Heroku is awesome")
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
try:
server = ThreadedTCPServer(('', PORT), myHandler)
print ('Started httpserver on port ' , PORT)
ip,port = server.server_address
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
allow_reuse_address = True
server.serve_forever()
except KeyboardInterrupt:
print ('CTRL + C RECEIVED - Shutting down the REST server')
server.socket.close()
When heroku runs your process, it defines the environment variable PORT to the internal port you should expose your server on. Your server will then be accessible from the internet on port 80, the default HTTP port.
Python can access environment variables with os.environ.
So you can use:
PORT = environ['PORT']
os.envron docs here
You can read more about how Heroku handles ports here.
Create a Procfile with a single line:
web: python yourscript.py
Building a simple file server using the SimpleHTTPServer module in Python, however I'm running into issues when trying to get the IP from a connecting client. Here is what I have..
import SimpleHTTPServer
Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", 8080), Handler)
print "Serving local directory"
while True:
httpd.handle_request()
print Handler.client_address[0]
When a client connects I get..
AttributeError: class SimpleHTTPRequestHandler has no attribute 'client_address'
I know this is because I haven't instantiated the class yet, but is there another way to get the IP from the client without having to create a handler instance? The client's IP is outputted to the console when a connection is made, I just need a way to grab that IP within my script.
Thanks!
Indeed, the Handler class object is unrelated to specific instances. Set up your own handler class, like this:
import SimpleHTTPServer
import SocketServer
class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def handle_one_request(self):
print(self.client_address[0])
return SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
print("Serving local directory")
httpd = SocketServer.TCPServer(("", 8080), MyHandler)
while True:
httpd.handle_request()