How do I kill SimpleHTTPServer from within a Python script?
Asked Answered
M

8

9

I am trying to use http.server to test all the links in a Python project. I can get my script to work if I start the server before running my script, and then the server stops when I close the terminal window. But I'd really like the script itself to start and stop the server.

I made a test script to simply start the server, get a page and prove the server is running, and then stop the server. I can't seem to get the pid of the server. When I try to kill the pid that this script reports after the script runs, I get a message that there is no such process; but the server is still running.

How do I get the correct pid for the server, or more generally how do I stop the server from the script?

import os
import requests
from time import sleep

# Start server, in background.
print("Starting server...")
os.system('python -m http.server &')
# Make sure server has a chance to start before making request.
sleep(1)

print "Server pid: "
os.system('echo $$')

url = 'http://localhost:8000/index.html'
print("Testing request: ", url)
r = requests.get(url)
print("Status code: ", r.status_code)
Malcolm answered 24/10, 2013 at 15:46 Comment(3)
You could use subprocess.Popen instead of os.system, it offers a lot of additional functionality including termination of the spawned subprocess. Or you could just import SimpleHTTPServer and use it directly in your script...Knowle
That is helpful, but do you mind saying a little more? The only references I seem to find about using SimpleHTTPServer talk about using it from the command line; I'm not quite sure how to stop and start the server from within a python script. I have also never used processes before, so I'm not quite sure how to start the server subprocess, and then stop it. Actively googling...Malcolm
The subprocess documentation has all the info you need, see the sections about the Popen constructor and using Popen objects - you could do something like server = Popen(["python", "-m", "SimpleHTTPServer"]) and then use server.terminate() or server.kill() to end the process. As for starting a SimpleHTTPServer in a script, just look at the if __name__ == "__main__": block in SimpleHTTPServer.py - just keep a reference to the BaseServer instance and close it (it's a TCPServer subclass, so the TCPServer docs should help).Knowle
R
10

Here is what I am doing:

import threading

try: 
    from http.server import HTTPServer, SimpleHTTPRequestHandler # Python 3
except ImportError: 
    from SimpleHTTPServer import BaseHTTPServer
    HTTPServer = BaseHTTPServer.HTTPServer
    from SimpleHTTPServer import SimpleHTTPRequestHandler # Python 2

server = HTTPServer(('localhost', 0), SimpleHTTPRequestHandler)
thread = threading.Thread(target = server.serve_forever)
thread.daemon = True
thread.start()

def fin():
    server.shutdown()

print('server running on port {}'.format(server.server_port))

# here is your program

If you call fin in your program, then the server shuts down.

Rog answered 24/10, 2013 at 18:50 Comment(6)
I am using Python 2 at the moment, but I'd love this to run on Python 3 as well. I got an error 'no module named HTTPServer', so I changed the Python 2 import to from BaseHTTPServer import HTTPServer. The import worked, but then I got an error that "__init__() got an unexpected keyword argument 'daemon'". I get this if I use my own version of this script, or if I just copy your script and fix the import. Any suggestions?Malcolm
I fixed the deamon argument with an additional line. This should work under Python 3 and 2 now - hopefully.Rog
Are you able to kill the server with Ctrl-C?Statement
@CiroSantilli巴拿馬文件六四事件法轮功 Since deamon is set to true, the program should finish once the main thread ends. Usually, the main thread can be terminated with Control+C.Rog
@Rog I didn't reproduce on Ubuntu 16.04, Python 2.7, gnome terminal. Nothing happens on Ctrl+C, the server just keeps running, and I have to close the terminal for it to stop.Statement
@CiroSantilli烏坎事件2016六四事件法轮功 There is an edit which points at the problem I regularily misspell "deamon" and "daemon". Now, I updated the code and it should work again for Python 2 and 3.Rog
S
3

A slight modification to User's code above:

import threading
try: 
  from http.server import HTTPServer, BaseHTTPRequestHandler # Python 3
except ImportError: 
  import SimpleHTTPServer
  from BaseHTTPServer import HTTPServer # Python 2
  from SimpleHTTPServer import SimpleHTTPRequestHandler as BaseHTTPRequestHandler
server = HTTPServer(('localhost', 0), BaseHTTPRequestHandler)
thread = threading.Thread(target = server.serve_forever)
thread.deamon = True
def up():
  thread.start()
  print('starting server on port {}'.format(server.server_port))
def down():
  server.shutdown()
  print('stopping server on port {}'.format(server.server_port))
Scanderbeg answered 11/2, 2015 at 19:47 Comment(0)
E
3

This is a closure solution to the problem. Works on python 3.

import os
import threading
import webbrowser
from http.server import HTTPServer, SimpleHTTPRequestHandler


def simple_http_server(host='localhost', port=4001, path='.'):

    server = HTTPServer((host, port), SimpleHTTPRequestHandler)
    thread = threading.Thread(target=server.serve_forever)
    thread.deamon = True

    cwd = os.getcwd()

    def start():
        os.chdir(path)
        thread.start()
        webbrowser.open_new_tab('http://{}:{}'.format(host, port))
        print('starting server on port {}'.format(server.server_port))

    def stop():
        os.chdir(cwd)
        server.shutdown()
        server.socket.close()
        print('stopping server on port {}'.format(server.server_port))

    return start, stop

simple_http_server which will return start and stop functions

>>> start, stop = simple_http_server(port=4005, path='/path/to/folder')

which you can use as

>>> start()
starting server on port 4005

127.0.0.1 - - [14/Aug/2016 17:49:31] "GET / HTTP/1.1" 200 -

>>> stop()
stopping server on port 4005
Epic answered 14/8, 2016 at 14:9 Comment(0)
P
3

Here's a context-flavored version which I prefer because it cleans up automatically and you can specify the directory to serve:

from contextlib import contextmanager
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread


@contextmanager
def http_server(host: str, port: int, directory: str):
    server = ThreadingHTTPServer(
        (host, port), partial(SimpleHTTPRequestHandler, directory=directory)
    )
    server_thread = Thread(target=server.serve_forever, name="http_server")
    server_thread.start()

    try:
        yield
    finally:
        server.shutdown()
        server_thread.join()


def usage_example():
    import time

    with http_server("127.0.0.1", 8087, "."):
        # now you can use the web server
        time.sleep(100)
Phalangeal answered 6/5, 2021 at 6:41 Comment(0)
M
1

I got this to run, but I'm curious to hear how this compares to User's answer above. I came up with this after looking at the accepted answer here.

import subprocess
import requests
import os
import signal
from time import sleep

print "Starting server..."
cmd = 'python -m SimpleHTTPServer'
pro = subprocess.Popen(cmd, shell=True, preexec_fn=os.setsid)

# Make sure server has a chance to start before making request.
sleep(1)

url = 'http://localhost:8000/index.html'
print "Testing request: ", url
r = requests.get(url)
print "Status code: ", r.status_code

os.killpg(pro.pid, signal.SIGTERM)
Malcolm answered 24/10, 2013 at 23:34 Comment(0)
P
0

My solution with browser opening:

File: http.py

import SimpleHTTPServer
import SocketServer
import threading
import webbrowser
import platform
from socket import SOL_SOCKET,SO_REUSEADDR

class HTTPServer():

    def __init__(self,port=8000,url='http://localhost'):
        self.port = port
        self.thread = None
        self.httpd = None                
        self.run = False
        self.url = url

        os = platform.system()
        if os=='Linux':
            self.browser_path = "/usr/bin/google-chrome %s"            
        elif os == 'Windows':
            self.browser_path = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s"   
        else:
            print("Chrome not found!")


    def start(self):        
        self.run = True     
        self.httpd = SocketServer.TCPServer(("", self.port), SimpleHTTPServer.SimpleHTTPRequestHandler)
        self.httpd.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        self.thread = threading.Thread(target = self._serve)
        self.thread.start()
        webbrowser.get(str(self.browser_path)).open(self.url+":"+str(self.port)+"/")


    def _serve(self):        
        while self.run:
            self.httpd.handle_request()

    def stop(self):
        self.run = False
        self.httpd.server_close()

After, just run:

from http import HTTPServer

server = HTTPServer()
server.start()

raw_input("Enter to close")

server.stop()
Pelagic answered 3/9, 2015 at 16:57 Comment(1)
Instead of opening Chrome you can use start %s on Windows and open %s on OSX (not sure for Linux) to open the default browser.Crural
A
0

In my opinion, what I did is:

  1. go to activity monitor
  2. then go to network
  3. search for word 'python'

(only if you ran the cmd/terminal command in python, because I think the way to stop it in cmd/terminal is ctrl+c or just quit the cmd/terminal)

note: the pid is also there

  1. quit the process 'python' (if it doesn't, use force quit process)
  2. if this works in cmd/terminal, then you are done! enjoii~

Hope this helps :D!!

Asuncionasunder answered 24/11, 2019 at 14:57 Comment(4)
And how is this an "internal" approach?Umbilicus
well I have the same problem as yours, so I searched what is PID in google and shows the result in google like this: google.com/… then I have an idea of just quitting it then it does work lolAsuncionasunder
You're approach is in essence giving the result via an external "forced" process. Teh destinction here is that OP is asking for a solution from within the serverscript. This can be done via threading routines or latest asyncio methods. I don't have that problem and merely reviewed your answer as it was flagged for "low quality answer".. and judging whether it was needed to be removed or not. I hope my comment helps you to carefully look at the question again and revaluate whether your answer provides the correct solution OP was asking for.Umbilicus
Ok, I understand :)Asuncionasunder
S
0

Using Python 3.8

import http.server
import socketserver
import threading
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
server = socketserver.TCPServer(("", PORT), Handler)
thread = threading.Thread(target = server.serve_forever)
thread.daemon = True
thread.start()
Shote answered 30/7, 2020 at 15:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.