I'm trying to create an multi-threaded web server using BaseHttpServer and ThreadingMixIn (as seen on various examples). Pseudo code would be something like:
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
pass
def do_POST(self):
pass
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
if __name__ == '__main__':
server = ThreadedHTTPServer(('localhost', 9999), Handler)
print 'Starting server, use <Ctrl-C> to stop'
server.serve_forever()
This works as expected, but my problem is that not every request gets a thread, but threading is done per URL. I've tested it like this: I have an URL bound to execute the following method:
import time
import datetime
def request_with_pause(self):
print datetime.datetime.now().strftime("%H:%M:%S.%f"), 'REQUEST RECEIVED'
time.sleep(10)
print datetime.datetime.now().strftime("%H:%M:%S.%f"), 'SENT RESPONSE'
It works fine, except when I call the url twice with a 5 second pause (click the URL, wait 5 seconds and click it another time) - both "responses" arrive after 10 seconds (response of first click).
In Python 2.7:
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from threading import Thread
class ThreadedHTTPServer(HTTPServer):
def process_request(self, request, client_address):
thread = Thread(target=self.__new_request, args=(self.RequestHandlerClass, request, client_address, self))
thread.start()
def __new_request(self, handlerClass, request, address, server):
handlerClass(request, address, server)
self.shutdown_request(request)
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write("hello world")
server = ThreadedHTTPServer(('', 80), Handler)
#server.serve_forever()
You can find the main source code of the HTTPServer class in SocketServer.py which you can find in the Lib folder in the Python directory. (HTTPServer is inherited from TCPServer, TCPServer is inherited from BaseServer.)
The important line is 315:
def process_request(self, request, client_address):
self.finish_request(request, client_address)
self.shutdown_request(request)
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self)
In this point the server create new request object with your Handler class. The BaseRequestHandler constructor automatically call the self.setup(), self.handle() and the self.finish() method.
So what I did was to override the process_request method to move this stuff in a new thread.
Related
I can't figure out how to make this small http server reply the get request and play a sound in parallel.
Current code, does not close the get request until the sound "winsound.PlaySound("SystemExit", winsound.SND_ALIAS)" end playing.
What i need is for the sound to be async to the request so that the get request ends asap and the sound keeps playing.
#!/usr/bin/env python3
from http.server import HTTPServer, SimpleHTTPRequestHandler, test
import socketserver
from urllib.parse import urlparse
from urllib.parse import parse_qs
import sys
import winsound
class MyHttpRequestHandler(SimpleHTTPRequestHandler):
try:
def do_GET(self):
# Sending an '200 OK' response
self.send_response(200)
# Setting the header
self.send_header("Content-type", "text/html")
# Whenever using 'send_header', you also have to call 'end_headers'
self.end_headers()
html = "ok"
# Writing the HTML contents with UTF-8
self.wfile.write(bytes(html, "utf8"))
winsound.PlaySound("SystemExit", winsound.SND_ALIAS)
return
except Exception as e:
print(str(e))
# Create an object of the above class
handler_object = MyHttpRequestHandler
PORT = 8000
my_server = socketserver.TCPServer(("", PORT), handler_object)
# Star the server
my_server.serve_forever()
Use a flag to run it async:
winsound.PlaySound("SystemExit", winsound.SND_ALIAS|winsound.SND_ASYNC)
If you wish to have tighter control, use concurrent.futures.ThreadPoolExecutor() to run it in a different thread:
from concurrent.futures import ThreadPoolExecutor
import winsound
pool = ThreadPoolExecutor()
class MyHttpRequestHandler(SimpleHTTPRequestHandler):
try:
def do_GET(self):
# Sending an '200 OK' response
self.send_response(200)
# Setting the header
self.send_header("Content-type", "text/html")
# Whenever using 'send_header', you also have to call 'end_headers'
self.end_headers()
html = "ok"
# Writing the HTML contents with UTF-8
self.wfile.write(bytes(html, "utf8"))
pool.submit(winsound.PlaySound, "SystemExit", winsound.SND_ALIAS)
return
except Exception as e:
print(str(e))
BACKGROUND
So basically, it's question about execution scope in Python. Back to your code above, in order for request to finish it has to be execute all task.
send response (this one doesn't make effect, because you have to return result from the method get)
setting headers
playing music
Obviously, you return at the end of the request execution and your thread monitor just one. So request will finish after finishing all the tasks.
SOLUTION
So as you mentioned in the question, you are right. To play music on your Server on the request, you have to run async task and let you request return result from get request. For ex. by using asyncio (it's very handy lib, so check it out)
import asyncio
import socketserver
from http.server import SimpleHTTPRequestHandler
import winsound
class MyHttpRequestHandler(SimpleHTTPRequestHandler):
try:
def do_GET(self):
self.send_response(200)
self.wfile.write(bytes("Ok", "utf8"))
# Shared Queue of Background Tasks
asyncio.get_event_loop().run_until_complete(
MyHttpRequestHandler.play_music()
)
except Exception as e:
print(str(e))
#staticmethod
async def play_music():
try:
print('Playing sound!')
winsound.PlaySound("SystemExit", winsound.SND_ALIAS)
# Maybe you want to add Error handling
finally:
pass
# Create an object of the above class
handler = MyHttpRequestHandler
server = socketserver.TCPServer(
("", 8000), handler)
# Star the server
server.serve_forever()
I am trying to write a simple multithreaded http server which answers requests after 5 sec.
This code does not work, two simultaneous requests take 10 sec to complete, and I don't understand why.
from socketserver import ThreadingMixIn
from http.server import SimpleHTTPRequestHandler, HTTPServer, BaseHTTPRequestHandler
class ThreadingSimpleServer(ThreadingMixIn, HTTPServer):
pass
import sys
import os
import time
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
time.sleep(5)
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
self.wfile.write("answer")
return
server = ThreadingSimpleServer(('', 8000), Handler)
try:
while 1:
sys.stdout.flush()
server.handle_request()
except KeyboardInterrupt:
print("Finished")
You are calling handle_request, which handles one request after the other. You have to use serve_forever, so that the server can handle request automatically.
I've written a simple HTTP server and made it into a Windows service using pywin32. The server successfully processes requests when run in the debugger, inside actual service it gets the request but hangs on send_response operation. What might be the reason?
from http.server import BaseHTTPRequestHandler, HTTPServer
import win32serviceutil
import win32service
import sys
PORT_NUMBER = 6363
class myHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(bytes("Hello World !", "utf-8"))
return
class TestSvc(win32serviceutil.ServiceFramework):
_svc_name_ = "TestSvc"
_svc_display_name_ = "Test Service"
_svc_description_ = "Tests server inside service."
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.server = HTTPServer(('', PORT_NUMBER), myHandler)
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
print('Started httpserver on port ', PORT_NUMBER)
self.server.serve_forever()
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.server.shutdown()
#sys.frozen = 'windows_exe'
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(TestSvc, argv=sys.argv)
Actually it was hanging when executing sys.stderr.write (which is the default logging output for BaseHTTPRequestHandler). So I've overridden log_message function in my request handler class and it works fine now.
I wrote a http server program in python, my RequestHandler class inherit from BaseHTTPServer, it init a member a, but I can't access it where BaseHTTPServer is first init statement, when I change the init statement order, it will be correct.
#!/usr/bin/env python
import BaseHTTPServer
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, request, client_address, server):
BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server)
self.a = 0
print 'aaaa'
def do_GET(self):
print self.a # will cause exception
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
server_address = ('127.0.0.1', 8080)
server_class = BaseHTTPServer.HTTPServer
handler_class = RequestHandler
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
when I changed __init__ order, it correct, why?
#!/usr/bin/env python
import BaseHTTPServer
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, request, client_address, server):
# change init order
print 'aaaa'
self.a = 0
BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server)
def do_GET(self):
print self.a # I got
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
server_address = ('127.0.0.1', 8080)
server_class = BaseHTTPServer.HTTPServer
handler_class = RequestHandler
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
Because the request is handled in BaseHTTPServer.BaseHTTPRequestHandler.__init__ (to be exact, superclass' __init__).
do_GET method is called while processing the request; At that time, self.a = 0 is not executed; cause the AttributeError.
In short, the do_GET method is called before the self.a line for the first code.
RequestHandler.__init__
BaseHTTPServer.BaseHTTPRequestHandler.__init__(..)
...
do_GET
...
self.a = 0
I would like to pass a Queue object to a base ThreadedHTTPServer implementation. My existing code works just fine, but I would like a safe way to send calls to and from my HTTP requests. Normally this would probably be handled by a web framework, but this is a HW limited environment.
My main confusion comes on how to pass the Queue (or any) object to allow access to other modules in my environment.
The base code template I have currently running:
import base64,threading,urlparse,urllib2,os,re,cgi,sys,time
import Queue
class DemoHttpHandler(BaseHTTPRequestHandler):
def __init__(self, request, client_address, server,qu):
BaseHTTPRequestHandler.__init__(self, request, client_address, server)
def do_GET(self):
...
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
def main():
listen_interface = "localhost"
listen_port = 2323
server = startLocalServer.ThreadedHTTPServer((listen_interface, listen_port), startLocalServer.DemoHttpHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
print 'started httpserver thread...'
Your code does not run but I modified it so that it will run:
import base64,threading,urlparse,urllib2,os,re,cgi,sys,time
import Queue
class DemoHttpHandler(BaseHTTPRequestHandler):
def __init__(self, request, client_address, server):
BaseHTTPRequestHandler.__init__(self, request, client_address, server)
self.qu = server.qu # save the queue here.
def do_GET(self):
...
self.qu # access the queue self.server.qu
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
def main():
listen_interface = "localhost"
listen_port = 2323
qu = Queue.Queue()
server = startLocalServer.ThreadedHTTPServer((listen_interface, listen_port), startLocalServer.DemoHttpHandler)
server.qu = qu # store the queue in the server
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
print 'started httpserver thread...'