1

Ok, I'm writing a pyqt software to generate a webpage. Due to some security issues with Chrome and other things, I need a webserver to test the webpage.

So I thought to create a button called run, that you can click or press f5 to start a server, and open the browser to the page. The snippet of code that this button calls, simplified (there is some code to do things, including changing current directory and such), looks like this:

import sys 
import webbrowser
from SimpleHTTPServer import SimpleHTTPRequestHandler as HandlerClass
from BaseHTTPServer import HTTPServer as ServerClass

Protocol = 'HTTP/1.0'
port = 8080
ip = '127.0.0.1'

new = 2 #goes to new tab
url = "http://"+ip+":{0}".format(port)

serverAddress = (ip,port)
HandlerClass.protocol = Protocol
httpd = ServerClass(serverAddress, HandlerClass)

sa = httpd.socket.getsockname()
webbrowser.open(url,new=new)
httpd.serve_forever()

Ok, the problem is as serve_forever is called, it can be expected to serve forever. Is there a way to kill the server after browser is closed?

Edit: I understand many people recommend using threads but I can't find a way to detect that the browser has closed or killing the thread in system monitor (I'm on Ubuntu) while testing.

Edit2: ok, I've read webbrowser.py, it doesn't seem to return any sort of process identifier...

Edit3: I'm reflecting on this, maybe the correct approach would be checking if someone is accessing the server, and if not, then kill it... This way I can detect if the tab was closed... Problem is the only way I can think uses a dummy page with this power that loads whatever page to test inside, which seems too hackish...

It seems if I can find a way of doing this, maybe through error responses...I can do a webserver in a subprocess that has a while and exits by itself like the one here: https://docs.python.org/2/library/basehttpserver.html#more-examples

import sys 
#from threading import Thread
import webbrowser
import BaseHTTPServer 
import SimpleHTTPServer

serverClass=BaseHTTPServer.HTTPServer
handlerClass=SimpleHTTPServer.SimpleHTTPRequestHandler

Protocol = "HTTP/1.0"
port = 8080
ip = '127.0.0.1'

new = 2 #2 goes to new tab, 0 same and 1 window.
url = "http://"+ip+":{0}".format(port)

handlerClass.protocol = Protocol
httpd = serverClass((ip,port), handlerClass)

sa = httpd.socket.getsockname()
print("\n---\nServing HTTP on {0}, port {1}\n---\n".format(sa[0],sa[1]) )
browserOk = webbrowser.open(url,new=new)

def runWhileTrue():
    while True:
        #print(vars(httpd))
        httpd.handle_request()

runWhileTrue()

Right now I'm thinking about using a timer like a watchdog, if the server is not used more then a period, it get's killed... But I think this is an awful solution... I wanted the browser to ping for it for some time while the tab is opened...maybe, don't know if optimal, looking this code right now : SimpleHTTPServer and SocketServer .

Thinking maybe if the server could understand a message from the website it could break loop. The tab closure could be detected in javascript like here : Browser/tab close detection using javascript (or any other language). Don't know how to communicate this to the server.

EditFinal:

In the javascript code of the webpage, I've inserted:

window.addEventListener('unload', function (e) { e.preventDefault(); jsonLevelGet("http://127.0.0.1:8081/exit.json");  }, false);

Then, the python code is this server.py:

import sys 
from threading import Thread
import webbrowser
import BaseHTTPServer 
import SimpleHTTPServer

serverClass=BaseHTTPServer.HTTPServer
handlerClass=SimpleHTTPServer.SimpleHTTPRequestHandler

Protocol = "HTTP/1.0"
port = 8080
ip = '127.0.0.1'
admIp = ip
admPort = 8081

new = 2 #2 goes to new tab, 0 same and 1 window.
url = "http://"+ip+":{0}".format(port)

handlerClass.protocol = Protocol
httpdGame = serverClass((ip,port), handlerClass)
httpdAdm = serverClass((admIp,admPort), handlerClass) 

sa = httpdGame.socket.getsockname()
sb = httpdAdm.socket.getsockname()
print("\n---\nServing HTTP on {0}, port {1}\n---\n".format(sa[0],sa[1]) )
print("\n---\nAdm HTTP listening on {0}, port {1}\n---\n".format(sb[0],sb[1]) )
browserOk = webbrowser.open(url,new=new)

def runGameServer():
    httpdGame.serve_forever()
    print("\nrunGameServer stopped\n")
    httpdAdm.shutdown()
    return

def runAdmServer():
    httpdAdm.handle_request()
    httpdGame.shutdown()
    print("\nrunAdmServer stopped\n")
    return

gameServerThread = Thread(target=runGameServer)
gameServerThread.daemon = True
admServerThread = Thread(target=runAdmServer)

gameServerThread.start()
admServerThread.start()
admServerThread.join()

It works! When the tab is closed, the server.py code exits! Thanks @st.never!

4
  • One way of doing it, even though it feels like a hack, is: you can manually check in your process list, and if the browser process is no longer present, you can call httpd.shutdown(). Commented Oct 10, 2014 at 23:51
  • I think I need at least pid of the process or else I will close only if no browsers are available. I'm thinking about extending the webbrowser class to return pid instead of True after browser process creation. Commented Oct 11, 2014 at 0:22
  • making it multi-platform compatible will be interesting Commented Oct 11, 2014 at 0:37
  • I see subprocess.Popen can provide a pid, but also the docs recommend to not use it in Python 2.7 - but I will try it out because I know it's included with every Python 2.7. I just need to sleep for now. Commented Oct 11, 2014 at 1:39

1 Answer 1

2

As you said, you could detect (in Javascript, in the browser) that the window is being closed, and send one last request to the server to shut it down.

If you don't want to inspect all the requests searching for the "poweroff request", you can instead have your server listen on two different ports (probably on different threads). For example, the "main" server listens on port 8080 with the current behaviour, and a separate instance listens on port 8081. Then you can simply shut down the server whenever any request reaches port 8081.

Sign up to request clarification or add additional context in comments.

1 Comment

This won't work on mobile browsers though or if the browser process was killed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.