diff options
Diffstat (limited to 'http2/httppages.py')
-rw-r--r-- | http2/httppages.py | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/http2/httppages.py b/http2/httppages.py new file mode 100644 index 0000000..3f90093 --- /dev/null +++ b/http2/httppages.py @@ -0,0 +1,705 @@ +import os, sys, random +from cStringIO import StringIO +import socket, time + +PLAYERNAMES = ['Bub', 'Bob', 'Boob', 'Beb', + 'Biob', 'Bab', 'Bib', + 'Baub', 'Beab', 'Biab'] + +try: + FILE = __file__ +except NameError: + FILE = sys.argv[0] +LOCALDIR = os.path.abspath(os.path.dirname(FILE)) + +sys.path.insert(0, os.path.abspath(os.path.join(LOCALDIR, os.pardir))) +sys.path.insert(0, os.path.abspath(os.path.join(LOCALDIR, os.pardir,'common'))) +import gamesrv, httpserver, hostchooser +from metaserver import metaclient +from httpserver import HTTPRequestError + + +class Options: + def __init__(self, dict={}): + self.update(dict) + def dict(self): + return self.__dict__.copy() + def update(self, dict): + self.__dict__.update(dict) + def copy(self): + return Options(self.__dict__) + def clear(self): + self.__dict__.clear() + def __getattr__(self, attr): + if not attr.startswith('_'): + return None + else: + raise AttributeError, attr + + +class PageServer: + CONFIGFILE = 'config.txt' + localservers = None + + def __init__(self, Game): + self.Game = Game + self.seed = hex(random.randrange(0x1000, 0x10000)) + #self.unique_actions = {} + self.localhost = gamesrv.HOSTNAME + self.filename = os.path.join(LOCALDIR, self.CONFIGFILE) + data = self.loadoptionfile() + self.globaloptions = Options(data.get('*', {})) + self.localoptions = Options(data.get(self.localhost, {})) + self.reloadports() + #self.inetserverlist = None + #self.inetservers = {} + #self.has_been_published = 0 + + def registerpages(self): + prefix = '%s/' % self.seed + #httpserver.register('controlcenter.html', self.controlcenterloader) + httpserver.register(prefix, self.indexloader) + httpserver.register(prefix+'index.html', self.indexloader) + #httpserver.register(prefix+'list.html', self.listloader) + httpserver.register(prefix+'new.html', self.newloader) + httpserver.register(prefix+'run.html', self.runloader) + httpserver.register(prefix+'stop.html', self.stoploader) + httpserver.register(prefix+'join.html', self.joinloader) + #httpserver.register(prefix+'register.html',self.registerloader) + httpserver.register(prefix+'options.html', self.optionsloader) + httpserver.register(prefix+'name.html', self.nameloader) + for fn in os.listdir(os.path.join(LOCALDIR, 'data')): + path = prefix + fn + if not httpserver.is_registered(path): + httpserver.register(path, httpserver.fileloader( + os.path.join(LOCALDIR, 'data', fn))) + + def opensocket(self): + hs = gamesrv.openhttpsocket() + if hs is None: + return 0 + self.httpport = port = gamesrv.displaysockport(hs) + self.indexurl = 'http://127.0.0.1:%d/%s/' % (port, self.seed) + if self.Game: + print self.Game.FnDesc, + print 'server is ready at', self.indexurl + return 1 + + def getlocalservers(self): + if self.localservers is None: + self.searchlocalservers() + return self.localservers + + def searchlocalservers(self): + servers = hostchooser.find_servers().items() + servers = filter(self.filterserver, servers) + servers.sort() + self.localservers = servers + +## def parse_inetserv(self, s): +## try: +## host, port, udpport, httpport = s.split(':') +## return host, int(port), int(udpport) +## except (ValueError, IndexError): +## return None, None, None + +## def getinetservers(self): +## if self.inetserverlist is None: +## return None +## result = [] +## for s in self.inetserverlist: +## host, port, udpport = self.parse_inetserv(s) +## addr = host, port +## if addr in self.inetservers: +## result.append((addr, self.inetservers[addr])) +## return result + +## def setinetserverlist(self, lst): +## self.inetserverlist = lst + +## def checkinetserverlist(self): +## ulist = [] +## for s in self.inetserverlist: +## host, port, udpport = self.parse_inetserv(s) +## if host is not None: +## ulist.append((host, udpport)) +## srvs = hostchooser.find_servers(ulist, delay=0.8) +## self.inetservers = {} +## for srv in srvs.items(): +## if not self.filterserver(srv): +## continue +## (host, port), info = srv +## try: +## host = socket.gethostbyaddr(host)[0] +## except socket.error: +## pass +## self.inetservers[host, port] = info +## #print 'hostchooser:', self.inetserverlist, '->', self.inetservers + + def filterserver(self, ((host, port), info)): + for c in host+str(port): + if c not in "-.0123456789:@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_abcdefghijklmnopqrstuvwxyz": + return 0 + return 1 + +## def statusservers(self): +## result = [], [] +## for s in self.inetserverlist: +## host, port, udpport = self.parse_inetserv(s) +## addr = host, port +## found = addr in self.inetservers +## result[found].append(s) +## return result + + def loadoptionfile(self): + try: + f = open(self.filename, 'r') + data = f.read().strip() + f.close() + except IOError: + data = None + return eval(data or '{}', {}, {}) + + def saveoptions(self): + data = self.loadoptionfile() + data['*'] = self.globaloptions.dict() + data[self.localhost] = self.localoptions.dict() + try: + f = open(self.filename, 'w') + print >> f, `data` + f.close() + except IOError, e: + print >> sys.stderr, "! Cannot save config file: " + str(e) + + def reloadports(self): + import msgstruct + msgstruct.PORTS.clear() + for key, value in self.localoptions.dict().items(): + if key.startswith('port_'): + key = key[5:] + if key == 'CLIENT' and type(value) == str and ':' in value: + udphostname, value = value.split(':') + msgstruct.PORTS['sendudpto'] = udphostname + try: + value = int(value) + except: + continue + msgstruct.PORTS[key] = value + + def startgame(self): + self.reloadports() + options = self.globaloptions + kwds = {} + if options.beginboard is not None: + kwds['beginboard'] = int(options.beginboard) + if options.lvlend is not None and options.lvlend.startswith('n'): + kwds['finalboard'] = int(options.finalboard) + if options.stepboard is not None: + kwds['stepboard'] = int(options.stepboard) + if options.limit is not None and options.limit.startswith('y'): + kwds['limitlives'] = int(options.lives) + if options.limitlifegain is not None: + kwds['lifegainlimit'] = int(options.lifegainlimit) + if options.extralife is not None: + kwds['extralife'] = int(options.extralife) + if options.autoreset is not None: + kwds['autoreset'] = options.autoreset.startswith('y') + if options.metapublish is not None: + kwds['metaserver'] = options.metapublish.startswith('y') + self.Game(options.file, **kwds) + + ### loaders ### + + def metaserverpage(self, headers): + metaserver_url = metaclient.METASERVER_URL + myhost = my_host(headers) + joinurl = quote_plus('%s/%s' % (myhost, self.seed)) + return metaserver_url + '?join=%s&time=%s' % (joinurl, time.time()) + + def mainpage(self, headers, juststarted=0, justconnected=0): + running = my_server() + count = len(gamesrv.clients) + tim = time.time() + #if running: + # metapublish = my_server_meta_address() + # fndesc = quote_plus(gamesrv.game.FnDesc) + #else: + # metapublish = None + return httpserver.load(os.path.join(LOCALDIR, 'data', 'index.html'), + 'text/html', locals=locals()) + + def indexloader(self, headers, cheat=[], **options): + if cheat: + import __builtin__ + for c in cheat: + getattr(__builtin__, '__cheat')(c) + else: + self.localservers = None + return self.mainpage(headers, juststarted=('juststarted' in options)) + + def controlcenterloader(self, headers, **options): + host = headers['remote host'] + host = socket.gethostbyname(host) + if host != '127.0.0.1': + raise HTTPRequestError, "Access denied" + return None, self.indexurl + +## def listloader(self, headers, s=[], **options): +## self.setinetserverlist(s) +## self.checkinetserverlist() +## query = [] +## missing, found = self.statusservers() +## for s in missing: +## query.append('d=' + s) +## for s in found: +## query.append('a=' + s) +## return self.mainpage(headers, query) + + def newloader(self, headers, **options): + if not self.Game: + raise HTTPRequestError, "Complete bub-n-bros installation needed" + locals = { + 'Game': self.Game, + 'options': self.globaloptions, + 'running': gamesrv.game is not None, + } + return httpserver.load(os.path.join(LOCALDIR, 'data', 'new.html'), + 'text/html', locals=locals) + + def runloader(self, headers, **options): + self.globaloptions.metapublish = 'n' + self.globaloptions.autoreset = 'n' + for key, value in options.items(): + if len(value) == 1: + setattr(self.globaloptions, key, value[0]) + self.saveoptions() + self.startgame() + return None, 'index.html?juststarted=%s' % time.time() + + def stoploader(self, headers, really=[], **options): + count = len(gamesrv.clients) + if count == 0 or really: + locals = { + 'self': self, + #'metaserver': METASERVER, + #'metapublish': gamesrv.game and my_server_meta_address(), + #'localdir': LOCALDIR, + } + gamesrv.closeeverything() + return httpserver.load(os.path.join(LOCALDIR, 'data', 'stop.html'), + 'text/html', locals=locals) + else: + locals = { + 'count': count, + } + return httpserver.load(os.path.join(LOCALDIR, 'data', 'confirm.html'), + 'text/html', locals=locals) + +## def registerloader(self, headers, a=[], d=[], **options): +## if a: # the lists 'a' and 'd' contain dummies !! +## self.globaloptions.metapublish = 'y' +## self.has_been_published = 1 +## kwd = 'a' +## else: +## self.globaloptions.metapublish = 'n' +## kwd = 'd' +## url = "%s?cmd=register&%s=%s" % (METASERVER, +## kwd, my_server_meta_address()) +## if a and gamesrv.game: +## url += '&desc=' + quote_plus(gamesrv.game.FnDesc) +## return None, url + + def joinloader(self, headers, host=[], port=[], httpport=[], + m=[], **options): + args = self.buildclientoptions() + assert len(host) == 1 + host = host[0] + if len(port) == 1: + port = port[0] + else: + try: + host, port = host.split(':') + except: + port = None + if args is None: + # redirect to the Java applet + try: + httpport = int(httpport[0]) + except (ValueError, IndexError): + if port: + raise HTTPRequestError, "This server is not running HTTP." + else: + raise HTTPRequestError, "Sorry, I cannot connect the Java applet to a server using this field." + return None, 'http://%s:%s/' % (host, httpport) + + # now is a good time to generate the color files if we can + file = os.path.join(LOCALDIR, os.pardir, 'bubbob', 'images', + 'buildcolors.py') + if os.path.exists(file): + g = {'__name__': '__auto__', '__file__': file} + execfile(file, g) + + if port: + address = '%s:%s' % (host, port) + else: + address = host + nbclients = len(gamesrv.clients) + script = os.path.join(LOCALDIR, os.pardir, 'display', 'Client.py') + script = no_quote_worries(script) + if m: + args.insert(0, '-m') + args = [script] + args + [address] + schedule_launch(args) + if m: + time.sleep(0.5) + s = 'Connecting to %s.' % address + return None, self.metaserverpage(headers) + '&head=' + quote_plus(s) + #elif my_server_address() == address: + # endtime = time.time() + 3.0 + # while gamesrv.recursiveloop(endtime, []): + # if len(gamesrv.clients) > nbclients: + # break + return self.mainpage(headers, justconnected=1) + + def optionsloader(self, headers, reset=[], savetime=[], **options): + if reset: + self.localoptions.clear() + self.globaloptions.clear() + self.saveoptions() + elif savetime: + self.localoptions.port_CLIENT = None + self.localoptions.port_LISTEN = None + self.localoptions.port_HTTP = None + for key, value in options.items(): + setattr(self.localoptions, key, value[0]) + self.saveoptions() + locals = { + 'self' : self, + 'options': self.localoptions, + } + return httpserver.load(os.path.join(LOCALDIR, 'data', 'options.html'), + 'text/html', locals=locals) + + def nameloader(self, headers, **options): + MAX = len(PLAYERNAMES) + if options: + anyname = None + for id in range(MAX): + keyid = 'player%d' % id + if keyid in options: + value = options[keyid][0] + anyname = anyname or value + teamid = 'team%d' % id + if teamid in options: + team = options[teamid][0] + if len(team) == 1: + value = '%s (%s)' % (value, team) + setattr(self.localoptions, keyid, value) + if 'c' in options: + for id in range(MAX): + keyid = 'player%d' % id + try: + delattr(self.localoptions, keyid) + except AttributeError: + pass + if 'f' in options: + for id in range(MAX): + keyid = 'player%d' % id + if not getattr(self.localoptions, keyid): + setattr(self.localoptions, keyid, + anyname or PLAYERNAMES[id]) + else: + anyname = getattr(self.localoptions, keyid) + self.saveoptions() + if 's' in options: + return self.mainpage(headers) + locals = { + 'options': self.localoptions.dict(), + } + return httpserver.load(os.path.join(LOCALDIR, 'data', 'name.html'), + 'text/html', locals=locals) + + def graphicmodeslist(self): + try: + return self.GraphicModesList + except AttributeError: + import display.modes + self.GraphicModesList = display.modes.graphicmodeslist() + javamode = display.modes.GraphicMode( + 'java', 'Java Applet (for Java browsers)', []) + javamode.low_priority = 1 + javamode.getmodule = lambda : None + self.GraphicModesList.insert(0, javamode) + return self.GraphicModesList + + def soundmodeslist(self): + try: + return self.SoundModesList + except AttributeError: + import display.modes + self.SoundModesList = display.modes.soundmodeslist() + return self.SoundModesList + + def localmodes(self): + import display.modes + currentmodes = [] + options = self.localoptions + for name, lst in [(options.dpy_, self.graphicmodeslist()), + (options.snd_, self.soundmodeslist())]: + try: + mode = display.modes.findmode(name, lst) + except KeyError: + try: + mode = display.modes.findmode(None, lst) + except KeyError, e: + print >> sys.stderr, str(e) # no mode! + mode = None + currentmodes.append(mode) + return currentmodes + + def buildclientoptions(self): + dpy, snd = self.localmodes() + if dpy.getmodule() is None: + return None # redirect to the Java applet + if dpy is None or snd is None: + raise HTTPRequestError, "No installed graphics or sounds drivers. See the settings page." + options = self.localoptions + result = ['--cfg='+no_quote_worries(self.filename)] + for key, value in options.dict().items(): + if key.startswith('port_') and value: + result.append('--port') + result.append('%s=%s' % (key[5:], value)) + if options.datachannel == 'tcp': result.append('--tcp') + if options.datachannel == 'udp': result.append('--udp') + if options.music == 'no': result.append('--music=no') + for optname, mode in [('--display', dpy), + ('--sound', snd)]: + result.append(optname + '=' + mode.name) + uid = mode.unique_id() + '_' + for key, value in options.dict().items(): + if key.startswith(uid): + result.append('--%s=%s' % (key[len(uid):], value)) + return result + +def my_host(headers): + return headers.get('host') or httpserver.my_host() + +def my_server(): + if gamesrv.game: + s = gamesrv.opentcpsocket() + return ((gamesrv.HOSTNAME, gamesrv.displaysockport(s)), + gamesrv.game.FnDesc) + else: + return None + +def my_server_address(): + running = my_server() + if running: + (host, port), info = running + return '%s:%d' % (host, port) + else: + return None + +##def my_server_meta_address(): +## s = gamesrv.opentcpsocket() +## ps = gamesrv.openpingsocket() +## hs = gamesrv.openhttpsocket() +## fullname = gamesrv.HOSTNAME +## try: +## fullname = socket.gethostbyaddr(fullname)[0] +## except socket.error: +## pass +## return '%s:%s:%s:%s' % (fullname, +## gamesrv.displaysockport(s), +## gamesrv.displaysockport(ps), +## gamesrv.displaysockport(hs)) + +##def meta_register(): +## # Note: this tries to open a direct HTTP connection to the meta-server +## # which may not work if the proxy is not configured in $http_proxy +## try: +## import urllib +## except ImportError: +## print >> sys.stderr, "cannot register with the meta-server: Python's urllib missing" +## return +## print "registering with the meta-server...", +## sys.stdout.flush() +## addr = my_server_meta_address() +## try: +## f = urllib.urlopen('%s?a=%s&desc=%s' % ( +## METASERVER, addr, quote_plus(gamesrv.game.FnDesc))) +## f.close() +## except Exception, e: +## print +## print >> sys.stderr, "cannot contact the meta-server (check $http_proxy):" +## print >> sys.stderr, "%s: %s" % (e.__class__.__name__, e) +## else: +## print "ok" +## unregister_at_exit(addr) + +##def meta_unregister(addr): +## import urllib +## print "unregistering from the meta-server...", +## sys.stdout.flush() +## try: +## f = urllib.urlopen(METASERVER + '?d=' + addr) +## f.close() +## except Exception, e: +## print "failed" +## else: +## print "ok" + +##def unregister_at_exit(addr, firsttime=[1]): +## if firsttime: +## import atexit +## atexit.register(meta_unregister, addr) +## del firsttime[:] + +QuoteTranslation = {} +for c in ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789' '_.-'): + QuoteTranslation[c] = c +del c +QuoteTranslation[' '] = '+' + +def quote_plus(s): + """Quote the query fragment of a URL; replacing ' ' with '+'""" + getter = QuoteTranslation.get + return ''.join([getter(c, '%%%02X' % ord(c)) for c in s]) + + +def main(Game, save_url_to=None, quiet=0): + #gamesrv.openpingsocket(0) # try to reserve the standard UDP port + srv = PageServer(Game) + srv.registerpages() + if not srv.opensocket(): + print >> sys.stderr, "server aborted." + sys.exit(1) + if quiet: + if Game: + Game.Quiet = 1 + import stdlog + f = stdlog.LogFile() + if f: + print "Logging to", f.filename + sys.stdout = sys.stderr = f + if save_url_to: + data = srv.indexurl + '\n' + def try_to_unlink(fn): + try: + os.unlink(fn) + except: + pass + import atexit + atexit.register(try_to_unlink, save_url_to) + try: + fno = os.open(save_url_to, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, + 0600) + if os.write(fno, data) != len(data): + raise OSError + os.close(fno) + except: + f = open(save_url_to, 'w') + f.write(data) + f.close() + #if webbrowser: + # srv.launchbrowser() + + +# ____________________________________________________________ +# Hack hack hack - workaround for the fact that on Windows +# the socket is inherited by the subprocess, which is quite +# bad because it keeps the browser-server connexion alive +# and the browser gets confused + + +def schedule_launch(args): + httpserver.actions_when_finished.append(lambda args=args: launch(args)) + +def launch(args): + # platform-specific hacks + print 'Running client -> ', ' '.join(args) + if 0: # OLD CODE sys.platform == 'darwin': # must start as a UI process + import tempfile + cmdname = tempfile.mktemp('_BubBob.py') + f = open(cmdname, 'w') + print >> f, 'import sys, os' + print >> f, 'try: os.unlink(%r)' % cmdname + print >> f, 'except OSError: pass' + print >> f, 'sys.argv[:] = %r' % (args,) + print >> f, '__file__ = %r' % cmdname + print >> f, 'execfile(%r)' % args[0] + f.close() + os.system('/usr/bin/open -a PythonLauncher "%s"' % cmdname) + else: + args.insert(0, sys.executable) + # try to close the open fds first + if hasattr(os, 'fork'): + try: + from resource import getrlimit, RLIMIT_NOFILE, error + except ImportError: + pass + else: + try: + soft, hard = getrlimit(RLIMIT_NOFILE) + except error: + pass + else: + if os.fork(): + return # in parent -- done, continue + # in child + for fd in range(3, min(16384, hard)): + try: + os.close(fd) + except OSError: + pass + os.execv(args[0], args) + # this point should never be reached + # fall-back + # (quoting sucks on Windows) ** 42 + if sys.platform == 'win32': + args[0] = '"%s"' % (args[0],) + if hasattr(os, 'P_DETACH'): + mode = os.P_DETACH + elif hasattr(os, 'P_NOWAIT0'): + mode = os.P_NOWAIT0 + else: + mode = os.P_NOWAIT + os.spawnv(mode, sys.executable, args) + +if sys.platform != "win32": + def no_quote_worries(s): + return s +else: + def no_quote_worries(s): # quoting !&?+*:-( + s = os.path.normpath(os.path.abspath(s)) + absroot = os.path.join(LOCALDIR, os.pardir) + absroot = os.path.normpath(os.path.abspath(absroot)) + ROOTDIR = os.curdir + while os.path.normpath(os.path.abspath(ROOTDIR)) != absroot: + if ROOTDIR == os.curdir: + ROOTDIR = os.pardir + else: + ROOTDIR = os.path.join(ROOTDIR, os.pardir) + if len(ROOTDIR) > 200: + # cannot find relative path! try with absolute one anyway + ROOTDIR = absroot + break + assert s.startswith(absroot) + if absroot.endswith(os.sep): # 'C:\' + absroot = absroot[:-1] + assert s[len(absroot)] == os.sep + relpath = s[len(absroot)+1:] + result = os.path.join(ROOTDIR, relpath) + print "no_quote_worries %r => %r" % (s, result) + return result + + +if __name__ == '__main__': + if (len(sys.argv) != 3 or sys.argv[1] != '--quiet' or + not sys.argv[2].startswith('--saveurlto=')): + print >> sys.stderr, "This script should only be launched by BubBob.py." + sys.exit(2) + main(None, sys.argv[2][len('--saveurlto='):], quiet=1) + gamesrv.mainloop() |