def __init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport): BaseApp.__init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport) self.videoHTTPServer = VideoHTTPServer(VIDEOHTTP_LISTENPORT) self.videoHTTPServer.background_serve() self.videoHTTPServer.register(self.videoservthread_error_callback, self.videoservthread_set_status_callback) # Maps Downloads to a using InstanceConnection and streaminfo when it # plays. So it contains the Downloads in VOD mode for which there is # active interest from a plugin. # # At the moment each Download is used/owned by a single IC and a new # request for the same torrent will stop playback to the original IC # and resume it to the new user. # self.dusers = {} # If the BG Process is started by the plug-in notify it with an event startupEvent = win32event.CreateEvent(None, 0, 0, 'startupEvent') win32event.SetEvent(startupEvent) win32api.CloseHandle( startupEvent ) # TODO : is it possible to avoid importing win32api just to close an handler?
def setUp(self): """ unittest test setup code """ self.port = 6789 self.serv = VideoHTTPServer(self.port) self.serv.background_serve() self.serv.register(self.videoservthread_error_callback, self.videoservthread_set_status_callback) self.sourcefn = os.path.join("API", "file.wmv") # 82KB self.sourcesize = os.path.getsize(self.sourcefn)
def __init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport): self.videoHTTPServer = VideoHTTPServer(VIDEOHTTP_LISTENPORT) self.videoHTTPServer.register(self.videoservthread_error_callback, self.videoservthread_set_status_callback) self.videoHTTPServer.background_serve() #self.searchHTTPServer = MultiHTTPServer(VIDEOHTTP_LISTENPORT+1) #self.searchHTTPServer.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) self.searchHTTPServer = self.videoHTTPServer BaseApp.__init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport) # SEARCH:P2P # Maps a query ID to the original searchstr, timestamp and all hits (local + remote) self.id2hits = Query2HitsMap() # Maps a URL path received by HTTP server to the requested resource, # reading or generating it dynamically. # # For saving .torrents received in hits to P2P searches using # SIMPLE+METADATA queries self.tqueue = TimedTaskQueue(nameprefix="BGTaskQueue") self.searchmapper = SearchPathMapper(self.s, self.id2hits, self.tqueue) self.hits2anypathmapper = Hits2AnyPathMapper(self.s, self.id2hits) self.searchHTTPServer.add_path_mapper(self.searchmapper) self.searchHTTPServer.add_path_mapper(self.hits2anypathmapper) self.searchHTTPServer.background_serve() self.searchurl = 'http://127.0.0.1:' + str( self.searchHTTPServer.get_port()) + URLPATH_SEARCH_PREFIX # Maps Downloads to a using InstanceConnection and streaminfo when it # plays. So it contains the Downloads in VOD mode for which there is # active interest from a plugin. # # At the moment each Download is used/owned by a single IC and a new # request for the same torrent will stop playback to the original IC # and resume it to the new user. # self.dusers = {} self.approxplayerstate = MEDIASTATE_STOPPED self.counter = 0 # counter for the stats reported periodically self.interval = 120 # report interval if sys.platform == "win32": # If the BG Process is started by the plug-in notify it with an event startupEvent = win32event.CreateEvent(None, 0, 0, 'startupEvent') win32event.SetEvent(startupEvent) win32api.CloseHandle( startupEvent ) # TODO : is it possible to avoid importing win32api just to close an handler?
class HTTPSeeder: def __init__(self): self.videoHTTPServer = VideoHTTPServer(VIDEOHTTP_LISTENPORT) self.videoHTTPServer.register(self.videoservthread_error_callback, self.videoservthread_set_status_callback) self.videoHTTPServer.background_serve() # # VideoServer status/error reporting # def videoservthread_error_callback(self, e, url): print >>sys.stderr, "httpseed: Video server reported error", url, str(e) def videoservthread_set_status_callback(self, status): print >>sys.stderr, "httpseed: Video server sets status callback", status
def register(self,utility,preferredplaybackmode=None,closeextplayercallback=None): self.utility = utility # TEMPARNO: make sure only used for language strings self.preferredplaybackmode = preferredplaybackmode self.determine_playbackmode() if self.playbackmode == PLAYBACKMODE_INTERNAL: # The python-vlc bindings. Created only once at the moment, # as using MediaControl.exit() more than once with the raw interface # blows up the GUI. # from BaseLib.Video.VLCWrapper import VLCWrapper self.vlcwrap = VLCWrapper(self.utility.getPath()) self.supportedvodevents = [VODEVENT_START,VODEVENT_PAUSE,VODEVENT_RESUME] else: self.vlcwrap = None # Can't pause when external player self.supportedvodevents = [VODEVENT_START] if self.playbackmode != PLAYBACKMODE_INTERNAL or not USE_VLC_RAW_INTERFACE: # Start HTTP server for serving video to external player self.videohttpserv = VideoHTTPServer.getInstance(self.videohttpservport) # create self.videohttpserv.background_serve() self.videohttpserv.register(self.videohttpserver_error_callback,self.videohttpserver_set_status_callback) if closeextplayercallback is not None: self.closeextplayercallback = closeextplayercallback
class TestVideoHTTPServer(unittest.TestCase): """ Class for testing HTTP-based video server. Mainly HTTP range queries. """ def setUp(self): """ unittest test setup code """ self.port = 6789 self.serv = VideoHTTPServer(self.port) self.serv.background_serve() self.serv.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) self.sourcefn = os.path.join("API","file.wmv") # 82KB self.sourcesize = os.path.getsize(self.sourcefn) def tearDown(self): """ unittest test tear down code """ print >>sys.stderr,"test: Tear down, sleeping 10 s" time.sleep(10) def videoservthread_error_callback(self,e,url): """ Called by HTTP serving thread """ print >>sys.stderr,"test: ERROR",e,url self.assert_(False) def videoservthread_set_status_callback(self,status): """ Called by HTTP serving thread """ print >>sys.stderr,"test: STATUS",status # # Tests # def test_ranges(self): # Run single test, VideoHTTPServer is singleton at the moment and # doesn't like recreate. try: self.range_test(115,214,100) self.range_test(self.sourcesize-100,None,100) self.range_test(None,100,100) self.range_test(115,214,100,setset=True) except Exception,e: print_exc()
class TestVideoHTTPServer(unittest.TestCase): """ Class for testing HTTP-based video server. Mainly HTTP range queries. """ def setUp(self): """ unittest test setup code """ self.port = 6789 self.serv = VideoHTTPServer(self.port) self.serv.background_serve() self.serv.register(self.videoservthread_error_callback, self.videoservthread_set_status_callback) self.sourcefn = os.path.join("API", "file.wmv") # 82KB self.sourcesize = os.path.getsize(self.sourcefn) def tearDown(self): """ unittest test tear down code """ print >> sys.stderr, "test: Tear down, sleeping 10 s" time.sleep(10) def videoservthread_error_callback(self, e, url): """ Called by HTTP serving thread """ print >> sys.stderr, "test: ERROR", e, url self.assert_(False) def videoservthread_set_status_callback(self, status): """ Called by HTTP serving thread """ print >> sys.stderr, "test: STATUS", status # # Tests # def test_ranges(self): # Run single test, VideoHTTPServer is singleton at the moment and # doesn't like recreate. try: self.range_test(115, 214, 100) self.range_test(self.sourcesize - 100, None, 100) self.range_test(None, 100, 100) self.range_test(115, 214, 100, setset=True) except Exception, e: print_exc()
def __init__(self, redirectstderrout, appname, appversion, params, single_instance_checker, installdir, i2iport, sport, httpport): # Almost generic HTTP server self.videoHTTPServer = VideoHTTPServer(httpport) self.videoHTTPServer.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) BaseApp.__init__(self, redirectstderrout, appname, appversion, params, single_instance_checker, installdir, i2iport, sport) self.httpport = httpport # SEARCH:P2P # Maps a query ID to the original searchstr, timestamp and all hits (local + remote) self.id2hits = Query2HitsMap() # Maps a URL path received by HTTP server to the requested resource, # reading or generating it dynamically. # # For saving .torrents received in hits to P2P searches using # SIMPLE+METADATA queries self.tqueue = TimedTaskQueue(nameprefix="BGTaskQueue") self.searchmapper = SearchPathMapper(self.s,self.id2hits,self.tqueue) self.hits2anypathmapper = Hits2AnyPathMapper(self.s,self.id2hits) self.videoHTTPServer.add_path_mapper(self.searchmapper) self.videoHTTPServer.add_path_mapper(self.hits2anypathmapper) # WEB Interface # Maps a URL path received by HTTP server to the requested resource, # reading or generating it dynamically. self.webIFmapper = WebIFPathMapper(self, self.s) self.videoHTTPServer.add_path_mapper(self.webIFmapper) # Generic HTTP server start. Don't add mappers dynamically afterwards! self.videoHTTPServer.background_serve() # Maps Downloads to a using InstanceConnection and streaminfo when it # plays. So it contains the Downloads in VOD mode for which there is # active interest from a plugin. # # At the moment each Download is used/owned by a single IC and a new # request for the same torrent will stop playback to the original IC # and resume it to the new user. # self.dusers = {} self.approxplayerstate = MEDIASTATE_STOPPED self.counter = 0 # counter for the stats reported periodically self.interval = 120 # report interval self.iseedeadpeople = False if sys.platform == "win32": # If the BG Process is started by the plug-in notify it with an event try: startupEvent = win32event.CreateEvent( None, 0, 0, 'startupEvent' ) win32event.SetEvent( startupEvent ) win32api.CloseHandle( startupEvent ) # TODO : is it possible to avoid importing win32api just to close an handler? except: pass
def setUp(self): """ unittest test setup code """ self.port = 6789 self.serv = VideoHTTPServer(self.port) self.serv.background_serve() self.serv.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) self.sourcefn = os.path.join("API","file.wmv") # 82KB or 82948 bytes self.sourcesize = os.path.getsize(self.sourcefn)
def __init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport): self.videoHTTPServer = VideoHTTPServer(VIDEOHTTP_LISTENPORT) self.videoHTTPServer.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) self.videoHTTPServer.background_serve() #self.searchHTTPServer = MultiHTTPServer(VIDEOHTTP_LISTENPORT+1) #self.searchHTTPServer.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) self.searchHTTPServer = self.videoHTTPServer BaseApp.__init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport) # SEARCH:P2P # Maps a query ID to the original searchstr, timestamp and all hits (local + remote) self.id2hits = Query2HitsMap() # Maps a URL path received by HTTP server to the requested resource, # reading or generating it dynamically. # # For saving .torrents received in hits to P2P searches using # SIMPLE+METADATA queries self.tqueue = TimedTaskQueue(nameprefix="BGTaskQueue") self.searchmapper = SearchPathMapper(self.s,self.id2hits,self.tqueue) self.hits2anypathmapper = Hits2AnyPathMapper(self.s,self.id2hits) self.searchHTTPServer.add_path_mapper(self.searchmapper) self.searchHTTPServer.add_path_mapper(self.hits2anypathmapper) self.searchHTTPServer.background_serve() self.searchurl = 'http://127.0.0.1:'+str(self.searchHTTPServer.get_port())+URLPATH_SEARCH_PREFIX # Maps Downloads to a using InstanceConnection and streaminfo when it # plays. So it contains the Downloads in VOD mode for which there is # active interest from a plugin. # # At the moment each Download is used/owned by a single IC and a new # request for the same torrent will stop playback to the original IC # and resume it to the new user. # self.dusers = {} self.approxplayerstate = MEDIASTATE_STOPPED self.counter = 0 # counter for the stats reported periodically self.interval = 120 # report interval if sys.platform == "win32": # If the BG Process is started by the plug-in notify it with an event startupEvent = win32event.CreateEvent( None, 0, 0, 'startupEvent' ) win32event.SetEvent( startupEvent ) win32api.CloseHandle( startupEvent ) # TODO : is it possible to avoid importing win32api just to close an handler?
class BackgroundApp(BaseApp): def __init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport): BaseApp.__init__(self, redirectstderrout, appname, params, single_instance_checker, installdir, i2iport, sport) self.videoHTTPServer = VideoHTTPServer(VIDEOHTTP_LISTENPORT) self.videoHTTPServer.background_serve() self.videoHTTPServer.register(self.videoservthread_error_callback, self.videoservthread_set_status_callback) # Maps Downloads to a using InstanceConnection and streaminfo when it # plays. So it contains the Downloads in VOD mode for which there is # active interest from a plugin. # # At the moment each Download is used/owned by a single IC and a new # request for the same torrent will stop playback to the original IC # and resume it to the new user. # self.dusers = {} # If the BG Process is started by the plug-in notify it with an event startupEvent = win32event.CreateEvent(None, 0, 0, "startupEvent") win32event.SetEvent(startupEvent) win32api.CloseHandle( startupEvent ) # TODO : is it possible to avoid importing win32api just to close an handler? def OnInit(self): try: # Do common initialization BaseApp.OnInitBase(self) return True except Exception, e: print_exc() self.show_error(str(e)) self.OnExit() return False
def vod_ready_callback(d,event,params): print >>sys.stderr,"main: VOD ready callback called",currentThread().getName(),"###########################################################",params["mimetype"] """ f = open("video.avi","wb") while True: data = stream.read() print >>sys.stderr,"main: VOD ready callback: reading",type(data) print >>sys.stderr,"main: VOD ready callback: reading",len(data) if len(data) == 0: break f.write(data) f.close() stream.close() """ videoserv = VideoHTTPServer.getInstance() videoserv.set_inputstream('video/mpeg',params["stream"],None)
def register(self, utility, preferredplaybackmode=None, closeextplayercallback=None): self.utility = utility # TEMPARNO: make sure only used for language strings self.preferredplaybackmode = preferredplaybackmode self.determine_playbackmode() if self.playbackmode == PLAYBACKMODE_INTERNAL: # The python-vlc bindings. Created only once at the moment, # as using MediaControl.exit() more than once with the raw interface # blows up the GUI. # from BaseLib.Video.VLCWrapper import VLCWrapper self.vlcwrap = VLCWrapper(self.utility.getPath()) self.supportedvodevents = [ VODEVENT_START, VODEVENT_PAUSE, VODEVENT_RESUME ] else: self.vlcwrap = None # Can't pause when external player self.supportedvodevents = [VODEVENT_START] if self.playbackmode != PLAYBACKMODE_INTERNAL or not USE_VLC_RAW_INTERFACE: # Start HTTP server for serving video to external player self.videohttpserv = VideoHTTPServer.getInstance( self.videohttpservport) # create self.videohttpserv.background_serve() self.videohttpserv.register( self.videohttpserver_error_callback, self.videohttpserver_set_status_callback) if closeextplayercallback is not None: self.closeextplayercallback = closeextplayercallback
print >>sys.stderr,"main: VOD ready callback: reading",type(data) print >>sys.stderr,"main: VOD ready callback: reading",len(data) if len(data) == 0: break f.write(data) f.close() stream.close() """ videoserv = VideoHTTPServer.getInstance() videoserv.set_inputstream('video/mpeg',params["stream"],None) if __name__ == "__main__": videoserv = VideoHTTPServer.getInstance() # create videoserv.background_serve() s = Session() if sys.platform == 'win32': tdef = TorrentDef.load('bla.torrent') else: tdef = TorrentDef.load('/tmp/bla.torrent') dcfg = DownloadStartupConfig.get_copy_of_default() #dcfg.set_saveas('/arno') dcfg = DownloadStartupConfig.get_copy_of_default() dcfg.set_video_start_callback(vod_ready_callback) #dcfg.set_selected_files('MATRIX-XP_engl_L.avi') # play this video #dcfg.set_selected_files('field-trip-west-siberia.avi')
class BackgroundApp(BaseApp): def __init__(self, logdir, appname, appversion, params, single_instance_checker, installdir, i2iport, sport, httpport, ws_serverport, bt_port): # Running WebSocket server SockJS Tornado Router = SockJSRouter(WsConnection, '/websocket') ws_serv = WsServer(i2iport,ws_serverport) ws_serv.start() # Almost generic HTTP server self.videoHTTPServer = VideoHTTPServer(httpport) self.videoHTTPServer.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) # Running BitTorrent HTTP server self.q = Queue() self.p = Process(target=serveHTTP, args=(self.q,)) self.p.start() period_upnp() BaseApp.__init__(self, logdir, appname, appversion, params, single_instance_checker, installdir, i2iport, sport) self.httpport = httpport # SEARCH:P2P # Maps a query ID to the original searchstr, timestamp and all hits (local + remote) self.id2hits = Query2HitsMap() # Maps a URL path received by HTTP server to the requested resource, # reading or generating it dynamically. # # For saving .torrents received in hits to P2P searches using # SIMPLE+METADATA queries self.tqueue = TimedTaskQueue(nameprefix="BGTaskQueue") self.searchmapper = SearchPathMapper(self.s,self.id2hits,self.tqueue) self.hits2anypathmapper = Hits2AnyPathMapper(self.s,self.id2hits) self.videoHTTPServer.add_path_mapper(self.searchmapper) self.videoHTTPServer.add_path_mapper(self.hits2anypathmapper) # WEB Interface # Maps a URL path received by HTTP server to the requested resource, # reading or generating it dynamically. self.webIFmapper = WebIFPathMapper(self, self.s) self.videoHTTPServer.add_path_mapper(self.webIFmapper) # Generic HTTP server start. Don't add mappers dynamically afterwards! self.videoHTTPServer.background_serve() # Maps Downloads to a using InstanceConnection and streaminfo when it # plays. So it contains the Downloads in VOD mode for which there is # active interest from a plugin. # # At the moment each Download is used/owned by a single IC and a new # request for the same torrent will stop playback to the original IC # and resume it to the new user. # self.dusers = {} self.approxplayerstate = MEDIASTATE_STOPPED self.counter = 0 # counter for the stats reported periodically self.interval = 120 # report interval self.iseedeadpeople = False if sys.platform == "win32": # If the BG Process is started by the plug-in notify it with an event try: startupEvent = win32event.CreateEvent( None, 0, 0, 'startupEvent' ) win32event.SetEvent( startupEvent ) win32api.CloseHandle( startupEvent ) # TODO : is it possible to avoid importing win32api just to close an handler? except: pass def OnInit(self): try: # Do common initialization BaseApp.OnInitBase(self) # Arno, 2010-07-15: We try to detect browser presence by looking # at get_speed_info JSON request from Firefox statusbar. However. # these calls are unreliable, i.e., somethings the XmlHTTPRequest # at the client doesn't reach the server, although the server is # capable of replying to the request. Hence, we disable self-destruct # for now. if KILLONIDLE: print >>sys.stderr,time.asctime(),'-', "bg: Kill-on-idle test enabled" self.i2is.add_task(self.i2i_kill_on_browser_gone,IDLE_BEFORE_SELFKILL/2) else: print >>sys.stderr,time.asctime(),'-', "bg: Kill-on-idle test disabled" print >>sys.stderr,time.asctime(),'-', "bg: Awaiting commands" return True except Exception,e: print_exc() self.show_error(str(e)) self.OnExit() return False
def __init__(self): self.videoHTTPServer = VideoHTTPServer(VIDEOHTTP_LISTENPORT) self.videoHTTPServer.register(self.videoservthread_error_callback, self.videoservthread_set_status_callback) self.videoHTTPServer.background_serve()
class TestVideoHTTPServer(unittest.TestCase): """ Class for testing HTTP-based video server. Mainly HTTP range queries. """ def setUp(self): """ unittest test setup code """ self.port = 6789 self.serv = VideoHTTPServer(self.port) self.serv.background_serve() self.serv.register(self.videoservthread_error_callback,self.videoservthread_set_status_callback) self.sourcefn = os.path.join("API","file.wmv") # 82KB or 82948 bytes self.sourcesize = os.path.getsize(self.sourcefn) def tearDown(self): """ unittest test tear down code """ print >>sys.stderr,time.asctime(),'-', "test: Tear down, sleeping 10 s" time.sleep(10) def videoservthread_error_callback(self,e,url): """ Called by HTTP serving thread """ print >>sys.stderr,time.asctime(),'-', "test: ERROR",e,url self.assert_(False) def videoservthread_set_status_callback(self,status): """ Called by HTTP serving thread """ print >>sys.stderr,time.asctime(),'-', "test: STATUS",status # # Tests # def test_ranges(self): # Run single test, VideoHTTPServer is singleton at the moment and # doesn't like recreate. self.range_test(115,214,self.sourcesize) self.range_test(self.sourcesize-100,None,self.sourcesize) self.range_test(None,100,self.sourcesize) self.range_test(115,214,self.sourcesize,setset=True) # # Internal # def register_file_stream(self): stream = open(self.sourcefn,"rb") streaminfo = { 'mimetype': 'video/x-ms-wmv', 'stream': stream, 'length': self.sourcesize } self.serv.set_inputstream(streaminfo,"/stream") def get_std_header(self): msg = "GET /stream HTTP/1.1\r\n" msg += "Host: 127.0.0.1:"+str(self.port)+"\r\n" return msg def create_range_str(self,firstbyte,lastbyte): head = "" if firstbyte is not None: head += str(firstbyte) head += "-" if lastbyte is not None: head += str(lastbyte) return head def range_test(self,firstbyte,lastbyte,sourcesize,setset=False): print >>sys.stderr,time.asctime(),'-', "test: range_test:",firstbyte,lastbyte,sourcesize,"setset",setset self.register_file_stream() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1', self.port)) head = self.get_std_header() head += "Range: bytes=" head += self.create_range_str(firstbyte,lastbyte) if setset: # Make into set of byte ranges, VideoHTTPServer should refuse. head += ",0-99" head += "\r\n" head += "Connection: close\r\n" head += "\r\n" if firstbyte is not None and lastbyte is None: # 100- expfirstbyte = firstbyte explastbyte = self.sourcesize-1 elif firstbyte is None and lastbyte is not None: # -100 expfirstbyte = self.sourcesize-lastbyte explastbyte = self.sourcesize-1 else: expfirstbyte = firstbyte explastbyte = lastbyte # the amount of bytes actually requested. (Content-length) expsize = explastbyte - expfirstbyte + 1 print >>sys.stderr,time.asctime(),'-', "test: Expecting first",expfirstbyte,"last",explastbyte,"size",sourcesize s.send(head) # Parse header s.settimeout(10.0) while True: line = self.readline(s) print >>sys.stderr,time.asctime(),'-', "test: Got line",`line` if len(line)==0: print >>sys.stderr,time.asctime(),'-', "test: server closed conn" self.assert_(False) return if line.startswith("HTTP"): if not setset: # Python returns "HTTP/1.0 206 Partial Content\r\n" HTTP 1.0??? self.assert_(line.startswith("HTTP/1.")) self.assert_(line.find("206") != -1) # Partial content else: self.assert_(line.startswith("HTTP/1.")) self.assert_(line.find("416") != -1) # Requested Range Not Satisfiable return elif line.startswith("Content-Range:"): expline = "Content-Range: bytes "+self.create_range_str(expfirstbyte,explastbyte)+"/"+str(sourcesize)+"\r\n" self.assertEqual(expline,line) elif line.startswith("Content-Type:"): self.assertEqual(line,"Content-Type: video/x-ms-wmv\r\n") elif line.startswith("Content-Length:"): self.assertEqual(line,"Content-Length: "+str(expsize)+"\r\n") elif line.endswith("\r\n") and len(line) == 2: # End of header break data = s.recv(expsize) if len(data) == 0: print >>sys.stderr,time.asctime(),'-', "test: server closed conn2" self.assert_(False) return else: f = open(self.sourcefn,"rb") if firstbyte is not None: f.seek(firstbyte) else: f.seek(lastbyte,os.SEEK_END) expdata = f.read(expsize) f.close() self.assert_(data,expdata) try: # Readed body, reading more should EOF (we disabled persist conn) data = s.recv(10240) self.assert_(len(data) == 0) except socket.timeout: print >> sys.stderr,time.asctime(),'-', "test: Timeout, video server didn't respond with requested bytes, possibly bug in Python impl of HTTP" print_exc() def readline(self,s): line = '' while True: data = s.recv(1) if len(data) == 0: return line else: line = line+data if data == '\n' and len(line) >= 2 and line[-2:] == '\r\n': return line