2

I created a webserver class from some samples from net. While catching KeyboardInterrupt the scripts ends, but not executing post lines after KeyboardInterrupt. Tried code as follows

import threading
import time
import signal
from http.server import BaseHTTPRequestHandler, HTTPServer

class WebServer(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.host = "localhost"
        self.port = 8080

    def run(self):
        ws = HTTPServer((self.host, self.port), MyHander)
        print("WebServer started at Port:",self.port)
        try:
            ws.serve_forever()
        except KeyboardInterrupt:
            pass
        finally:
            ws.server_close()
            print("WebServer stopped")

class MyHander(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(bytes("<html><head><title>https://pythonbasics.org</title></head>", "utf-8"))
        self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
        self.wfile.write(bytes("<body>", "utf-8"))
        self.wfile.write(bytes("<p>This is an example web server.</p>", "utf-8"))
        self.wfile.write(bytes("</body></html>", "utf-8"))

webServer = WebServer()
webServer.start()

output while pressing control C

WebServer started at Port:
>>>  8080
KeyboardInterrupt
>>> 

I just started python coding. Kindly help

Riskhan
  • 4,434
  • 12
  • 50
  • 76
  • 1
    Your server appears to be running in a different thread, so you might just be interrupting that thread only, or the main thread only. – 9769953 Nov 20 '19 at 09:11
  • Does this answer your question? [Cannot kill Python script with Ctrl-C](https://stackoverflow.com/questions/11815947/cannot-kill-python-script-with-ctrl-c) – Maroun Nov 20 '19 at 09:18

2 Answers2

2

Keyboard interrupts are not passed to the threads outside of the main thread except through the signal package. In general this is more complicated than needs be for what you are trying to accomplish. If you want CTRL+C to kill the webServer thread, you need to do two things.

First make sure your main thread does end. I.e. if you main thread has run out of code to execute and is waiting for the other threads to finish, you are basically deadlocked. This is easy to fix with a while loop.

Second, handle the KeyboardInterrupt in the main thread and use that signal to shutdown the additional threads. Again, we just add this to the while loop.

As a side note, it seems that HTTPServer.shutdown hangs (at least on Windows), the code looks correct, but I think it might be an inherited name mangling issue. To resolve this, I bypass calling .shutdown and just set the attributes needed to kill the server manually.

import threading
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from time import sleep

class WebServer(threading.Thread):
    def __init__(self):
        super().__init__()
        self.host = "localhost"
        self.port = 8080
        self.ws = HTTPServer((self.host, self.port), MyHandler)

    def run(self):
        print("WebServer started at Port:", self.port)
        self.ws.serve_forever()

    def shutdown(self):
        # set the two flags needed to shutdown the HTTP server manually
        self.ws._BaseServer__is_shut_down.set()
        self.ws.__shutdown_request = True

        print('Shutting down server.')
        # call it anyway, for good measure...
        self.ws.shutdown()
        print('Closing server.')
        self.ws.server_close()
        print('Closing thread.')
        self.join()


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("<html><head><title>Title</title></head>", "utf-8"))
        self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
        self.wfile.write(bytes("<body>", "utf-8"))
        self.wfile.write(bytes("<p>This is an example web server.</p>", "utf-8"))
        self.wfile.write(bytes("</body></html>", "utf-8"))


if __name__=='__main__':
    webServer = WebServer()
    webServer.start()
    while True:
        try:
            sleep(1)
        except KeyboardInterrupt:
            print('Keyboard Interrupt sent.')
            webServer.shutdown()
            exit(0)

Running the file, accessing http://localhost:8080/hello+world, then sending CTRL+C looks like this:

(base) C:\Users\james>python serve.py
WebServer started at Port: 8080
127.0.0.1 - - [20/Nov/2019 12:53:16] "GET /hello+world HTTP/1.1" 200 -
Keyboard Interrupt sent.
Shutting down server.
Closing server.
Closing thread.
James
  • 32,991
  • 4
  • 47
  • 70
1

Python signal handlers are always executed in the main Python thread, even if the signal was received in another thread.

https://docs.python.org/3/library/signal.html#signals-and-threads

The above web server is running in a new thread, not the main thread; As a result, catching KeyboardInterrupt in WebSever.run() does not take effect at all.

Jacky1205
  • 3,273
  • 3
  • 22
  • 44
  • 1
    If you still want to handle `CTRL+C` signal in `WebServer.run()`, you have to run the WebServer in main thread. The simplest change is to use `webServer.run()` instead of `webServer.start()` – Jacky1205 Nov 20 '19 at 09:34
  • OK i will try implement – Riskhan Nov 21 '19 at 05:57