Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
class BackgroundApp(BaseApp):
    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 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, "bg: Kill-on-idle test enabled"
                self.i2is.add_task(self.i2i_kill_on_browser_gone,
                                   IDLE_BEFORE_SELFKILL / 2)
            else:
                print >> sys.stderr, "bg: Kill-on-idle test disabled"

            print >> sys.stderr, "bg: Awaiting commands"
            return True

        except Exception, e:
            print_exc()
            self.show_error(str(e))
            self.OnExit()
            return False
Beispiel #4
0
class BackgroundApp(BaseApp):

    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 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,"bg: Kill-on-idle test enabled"
                self.i2is.add_task(self.i2i_kill_on_browser_gone,IDLE_BEFORE_SELFKILL/2)
            else:
                print >>sys.stderr,"bg: Kill-on-idle test disabled"
            
            print >>sys.stderr,"bg: Awaiting commands"
            return True

        except Exception,e:
            print_exc()
            self.show_error(str(e))
            self.OnExit()
            return False
class BackgroundApp(BaseApp):

    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?

    def OnInit(self):
        try:
            # Do common initialization
            BaseApp.OnInitBase(self)
            print >>sys.stderr,time.asctime(),'-', "bg: Awaiting commands"
            return True

        except Exception,e:
            print_exc()
            self.show_error(str(e))
            self.OnExit()
            return False
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,"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.
        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,"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,"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,"test: Got line",`line`
            
            if len(line)==0:
                print >>sys.stderr,"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,"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,"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