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!

Community
  • 1
  • 1
eri0o
  • 2,285
  • 4
  • 27
  • 43
  • 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()`. – ashwinjv Oct 10 '14 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. – eri0o Oct 11 '14 at 00:22
  • making it multi-platform compatible will be interesting – ashwinjv Oct 11 '14 at 00: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. – eri0o Oct 11 '14 at 01:39

1 Answers1

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.

st.never
  • 11,723
  • 4
  • 20
  • 21