I have the following code to run a simple http server
from http.server import SimpleHTTPRequestHandler, HTTPServer
host = "localhost"
port = 8881
server_class = HTTPServer
httpd = server_class((host, port), SimpleHTTPRequestHandler)
print("http server is running {}:{}".format(host, port))
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
At some point in my code I'd like to access the underlying socket s of the server (which I assume has to acessible somehow) to do something like s.getsockname() for example. Is that possible?
You can access it as self.socket, like so.
httpd.socket.getsockname()
See the source code of the base class SocketServer for more info.
However, for this use case the proper way of doing it is httpd.server_address. In general, you should not try to use the raw socket. Also I would skip the server_class variable and just go HTTPServer((host, port), SimpleHTTPRequestHandler) to keep things simple.
Related
I have a basic python server up using http.server in Python 3, not simplehttpserver
import http.server
import socketserver
PORT = 8080
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
And I need to get the clients IP address when they send a request, can anyone help me.
Thank you in advance.
You can port that duplicate answer to Python 3 by fixing up the imports:
import http.server
import socketserver
class MyHandler(http.server.SimpleHTTPRequestHandler):
def handle_one_request(self):
print(self.client_address[0])
return super().handle_one_request()
httpd = socketserver.TCPServer(("", 8080), MyHandler)
while True:
httpd.handle_request()
I am using python http.server to init 2 instances at different ports and serve up a html file from a folder...
import http.server
import socketserver
import os
PORT1 = 8000
PORT2 = 8001
os.chdir("html/folder1/")
Handler1 = http.server.SimpleHTTPRequestHandler
os.chdir("../folder2/")
Handler2 = http.server.SimpleHTTPRequestHandler
httpd1 = socketserver.TCPServer(("", PORT1), Handler1)
httpd2 = socketserver.TCPServer(("", PORT2), Handler2)
print("serving at port", PORT1)
print("serving at port", PORT2)
httpd1.serve_forever()
httpd2.serve_forever()
This loads without errors but I am only able to load http://localhost:8000
Any ideas where I am going wrong?
The serve_forever method does just that...serves http requests, and never exits. So when you do this:
httpd1.serve_forever()
httpd2.serve_forever()
The second statement is never executed because the first never exits. Possibly you could make this work by putting each call to serve_forever in a dedicated thread, and then just waiting for all threads to complete, but there may be a better solution.
How can I use HTTPServer (or some other class) to set up an HTTP server that listens to a filesystem socket instead of an actual network socket? By "filesystem socket" I mean sockets of the AF_UNIX type.
HTTPServer inherits from SocketServer.TCPServer, so I think it's fair to say that it isn't intended for that use-case, and even if you try to work around it, you may run into problems since you are kind of "abusing" it.
That being said, however, it would be possible per se to define a subclass of HTTPServer that creates and binds Unix sockets quite simply, as such:
class UnixHTTPServer(HTTPServer):
address_family = socket.AF_UNIX
def server_bind(self):
SocketServer.TCPServer.server_bind(self)
self.server_name = "foo"
self.server_port = 0
Then, just pass the path you want to bind to by the server_address argument to the constructor:
server = UnixHTTPServer("/tmp/http.socket", ...)
Again, though, I can't guarantee that it will actually work well. You may have to implement your own HTTP server instead.
I followed the example from #Dolda2000 above in Python 3.5 and ran into an issue with the HTTP handler falling over with an invalid client address. You don't have a client address with Unix sockets in the same way that you do with TCP, so the code below fakes it.
import socketserver
...
class UnixSocketHttpServer(socketserver.UnixStreamServer):
def get_request(self):
request, client_address = super(UnixSocketHttpServer, self).get_request()
return (request, ["local", 0])
...
server = UnixSocketHttpServer((sock_file), YourHttpHandler)
server.serve_forever()
With these changes, you can perform an HTTP request against the Unix socket with tools such as cURL.
curl --unix-socket /run/test.sock http:/test
Overview
In case it help anyone else, I have created a complete example (made for Python 3.8) based on Roger Lucas's example:
Server
import socketserver
from http.server import BaseHTTPRequestHandler
class myHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
self.wfile.write(b"Hello world!")
return
class UnixSocketHttpServer(socketserver.UnixStreamServer):
def get_request(self):
request, client_address = super(UnixSocketHttpServer, self).get_request()
return (request, ["local", 0])
server = UnixSocketHttpServer(("/tmp/http.socket"), myHandler)
server.serve_forever()
This will listen on the unix socket and respond with "Hello World!" for all GET requests.
Client Request
You can send a request with:
curl --unix-socket /tmp/http.socket http://any_path/abc/123
Troubleshooting
If you run into this error:
OSError: [Errno 98] Address already in use
Then delete the socket file:
rm /tmp/http.socket
So I have a web server that I can run using python, but I have a question. Can I change the IP address where the server runs the only one I can get to work is 127.0.0.1 which is the localhost address? I have already tried with no luck I want to use a unused one on my network.
Here's the code:
import sys
import BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
HandlerClass = SimpleHTTPRequestHandler
ServerClass = BaseHTTPServer.HTTPServer
Protocol = "HTTP/1.0"
if sys.argv[1:]:
port = int(sys.argv[1])
else:
port = 8000
server_address = ('127.0.0.1', port)
HandlerClass.protocol_version = Protocol
httpd = ServerClass(server_address, HandlerClass)
sa = httpd.socket.getsockname()
print "Running server on", sa[0], "port", sa[1], "..."
httpd.serve_forever()
You can only use addresses that are bound to a network interface on the computer. You cannot use random addresses picked out of thin air.
I just encountered a weird issue about bottle on windows.
When I tested the my bottle codes, I found that it could run multiple same programs on WINDOWS using same address and port. But when you try to start multiple same program on the Linux or Mac using same address and port, it will report the below error:
socket.error: [Errno 48] Address already in use
my bottle codes are:
from bottle import route, run, template
#route('/hello/:name')
def index(name='World'):
return template('<b>Hello {{name}} </b>', name=name)
run(host='localhost', port=9999)
Then I traced the code, from bottle to wsgiref, and finnaly found that the problem might be in the Python27\Lib\BaseHTTPServer.py.
I mean when I use the the below simple codes:
import BaseHTTPServer
def run(server_class=BaseHTTPServer.HTTPServer,
handler_class=BaseHTTPServer.BaseHTTPRequestHandler):
server_address = ('localhost', 9999)
print "start server on localhost 9999"
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
run()
The same issue would happen on windows.
But if I directly used the socketserver, like the below codes:
import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print "{} wrote:".format(self.client_address[0])
print self.data
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
print "Start a server on localhost:9999"
# Create the server, binding to localhost on port 9999
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
The same issue will not happen, I mean even on window the above socketserver codes will report the error, when you try to start another programe.
socket.error: [Errno 48] Address already in use
All my tests used the Python 2.7, Windows 7 and Centos 5.
So my questions are why the HTTPServer will have this issue on windows?
And how can I let my bottle programe will report the same error on windows, just like on windows?
Sorry to bother all.
I've found the resolution, just so simple.
Simply change the BaseHTTPServer.HTTPServer's attribute allow_reuse_address to 0.
The codes should be:
from bottle import route, run, template
import BaseHTTPServer
#route('/hello/:name')
def index(name='World'):
return template('<b>Hello {{name}} </b>', name=name)
setattr(BaseHTTPServer.HTTPServer,'allow_reuse_address',0)
run(host='localhost', port=9999)