BaseHTTPServer mit wfile umgehen

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Sebi.Schneider
User
Beiträge: 38
Registriert: Freitag 3. Mai 2013, 15:05

Hallo Freunde,

Da BaseHTTPServer eine high-Level Bibliothek ist darf ich diese in meiner Aufgabe nicht benutzen.

Jedoch Brauch ich diese Zeile

Data = urlib2.urlopen(URL).readline()
Self.wfile.writelines(Data)


Wie kann ich das ändern ohne eine die basehttpserver Bibliothek zu benutzen

Vielen dank vorab
BlackJack

@Sebi.Schneider: Die beiden Zeilen verwenden `BaseHTTPServer` nicht. Warum taucht der dann in der Betreffzeile auf und warum erwähnst Du im Text unnötigerweise dass Du den nicht verwenden darfst? Was genau gefällt Dir denn an den beiden Zeilen nicht?
Sebi.Schneider
User
Beiträge: 38
Registriert: Freitag 3. Mai 2013, 15:05

Hallo @BlackJack, da es hierbei um eine Abgabe aus der Uni handelt, dürfen wir die high-level Klassen nicht verwenden :K
BlackJack

@Sebi.Schneider: Das tun die beiden Zeilen dort ja auch nicht. Ich sehe da jedenfalls keinen `BaseHTTPServer`, darum verstehe ich nicht warum der überhaupt im Betreff und im ersten Beitrag erwähnt wird. Damit ist die Frage total unklar. Und ohne klare Frage kann man schlecht Antworten geben. :-(
Sebi.Schneider
User
Beiträge: 38
Registriert: Freitag 3. Mai 2013, 15:05

@BlackJack sorry, ich wollte nicht den ganzen Code hier rein posten, natürlich importiere ich den BaseHTTPSERVER

Code: Alles auswählen

import BaseHTTPServer
import hashlib
import os
import urllib2

class CacheHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
      m = hashlib.md5()
      m.update(self.path)
      cache_filename = m.hexdigest()
      if os.path.exists(cache_filename):
          print "Cache hit"
          data = open(cache_filename).readlines()
      else:
          print "Cache miss"
          url=self.path
          print url
          data = urllib2.urlopen(url).readlines()
          open(cache_filename, 'wb').writelines(data)
      self.send_response(200)
      self.end_headers()
      self.wfile.writelines(data)

def run():
    server_address = ('localhost', 8080)
    httpd = BaseHTTPServer.HTTPServer(server_address, CacheHandler)
    httpd.serve_forever()

if __name__ == '__main__':
    run()
Zuletzt geändert von Anonymous am Donnerstag 27. Juni 2013, 11:21, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@Sebi.Schneider: Dann geht es ja nicht um die beiden Zeilen sondern um *alles*. Du sollst anscheinend einen Proxyserver selber programmieren. Das ist jetzt aber keine konkrete Frage mehr.

Was darfst Du denn verwenden und was nicht? Ist `httplib` erlaubt? Wie sieht es mit `urllib`/`urllib2` aus? Oder vielleicht ausschliesslich `socket`?
Sebi.Schneider
User
Beiträge: 38
Registriert: Freitag 3. Mai 2013, 15:05

ja genau, das urllib2 dürfen wir verwenden. Da hab ich auch extra gefragt :mrgreen:
BlackJack

@Sebi.Schneider: Und `httplib`? Darauf baut `urllib2` ja auch auf, also müsste es eigentlich erlaubt sein, wenn die „höhere” API in `urllib2` erlaubt ist.

Dann müsstest Du damit einen HTTP-Proxy implementieren. Empfohlener Lesestoff: RFCs die das HTTP beschreiben. Also aktuell RFC2616.
Sebi.Schneider
User
Beiträge: 38
Registriert: Freitag 3. Mai 2013, 15:05

@BlackJack danke für die Information.

Ich habe bereits ein kleinen webServer geschrieben, nur hab ich Probleme das wfile.writeline() von BASEHTTPSERVER in mein WebServer zu implementieren, da
ich diese ja benutzen darf.

Code: Alles auswählen

import sys, string, socket,thread,select,os,urllib2,hashlib

class ConnectionHandler:
    def __init__(self, connection, address, timeout):
        

        self.client = connection
        self.client_buffer = ''
        self.timeout = timeout
       
        self.method, self.path, self.protocol = self.get_base_header()
        if self.method=='GET':
            self.method_GET()
        elif self.method in ('OPTIONS', 'GET', 'HEAD', 'POST', 'PUT',
                             'DELETE', 'TRACE'):
            self.method_others()
        self.client.close()
        self.target.close()

    def get_base_header(self):
        while 1:
            self.client_buffer += self.client.recv(BUFLEN)
            
            end = self.client_buffer.find('\n')
            if end!=-1:
                break
        print '%s'%self.client_buffer[:end]#debug
        data = (self.client_buffer[:end+1]).split()
        self.client_buffer = self.client_buffer[end+1:]
        
  
        return data
      

    def method_GET(self):    
        m = hashlib.md5()
        m.update(self.path)
        cache_filename = m.hexdigest()
        if os.path.exists(cache_filename):
            print "Cache hit"
            data = open(cache_filename).readlines()
        else:
            print "Cache miss"
            url=self.path
            print url
            data = urllib2.urlopen(url).readlines()
            open(cache_filename, 'wb').writelines(data)
        self.path = self.path[7:]
        i = self.path.find('/')
        host = self.path[:i]  
        self._connect_target(host)
        path = self.path[i:]
        self.target.send('%s %s %s\n'%(self.method, path, self.protocol)+
                         self.client_buffer)
    
    def _connect_target(self, host):
        i = host.find(':')
        if i!=-1:
            port = int(host[i+1:])
            host = host[:i]
        else:
            port = 80
        (soc_family, _, _, _, address) = socket.getaddrinfo(host, port)[0]
        self.target = socket.socket(soc_family)
        self.target.connect(address)

 
 
def ordnererstellen(pfad):
    if not os.path.exists(pfad):
        os.mkdir(pfad)
    
def start(port,timeout,ressource,host ='localhost',IPv6 = False,handler= ConnectionHandler):
    pfad =os.path.dirname(os.path.abspath(sys.argv[0]))
    pfad+="/zwischenspeicher"
    ordnererstellen(pfad)
        
    print port,timeout,  host
    if IPv6==True:
        soc_type=socket.AF_INET6
    else:
        soc_type=socket.AF_INET
    soc = socket.socket(soc_type)
    soc.bind((host, port))
    print "Serving on %s:%d."%(host, port)#debug
    soc.listen(0)
    while 1:
        thread.start_new_thread(ConnectionHandler, soc.accept()+(timeout,))

if __name__ == '__main__':   
        if len(sys.argv) < 3:
            print 'Usage: %s <server-host> <server-port> <server-ressource>' % sys.argv[0]
        else:
            start(int(sys.argv[1]),int(sys.argv[2]),int(sys.argv[3]))
BlackJack

@Sebi.Schneider: Du solltest den Quelltext vielleicht mal etwas aufräumen. Zum Beispiel alles Unbenutze rauswerfen. Einiges von den Importen ist überflüssig. Das `address`-Argument der `ConnectionHandler.__init__()` wird nicht verwendet, und auch `ressource` und `handler` in `start()` nicht.

Die Methode `ConnectionHandler.method_others()` gibt es nicht. Dabei wäre es doch als ersten Schritt erstmal interessant einen funktionierenden, komplett transparenten Proxy zu schreiben und den zu testen, bevor man anfängt die Daten zu cachen und/oder zu verändern.

Das `thread`-Modul ist deprecated und sollte nicht mehr verwendet werden. Die Dokumentation verweist auf das `threading`-Modul.

In der `__init__` sollten alle Attribute definiert werden. `target` ist sehr undurchsichtig. Es wird am Ende der `__init__()` verwendet, aber die Methode die es definiert wird nur indirekt durch die `__init__()` aufgerufen. Das ist schlecht nachvollziehbar.

'GET' taucht in der Liste mit „anderen” HTTP-Kommandos auf, obwohl der Fall schon im ``if`` davor behandelt wird.

Statt ``while 1:`` ist ``while True:`` IMHO verständlicher.

`BUFLEN` wird nirgends definiert. Der Code ist so also gar nicht lauffähig.

Der Name `get_base_header()` ist auch nicht wirklich gut gewählt. An der Stelle sollte man vielleicht lieber eine Methode schreiben die eine Zeile liest, also beim nächsten Aufruf das auch die nächste Zeile zurück gibt. An der Stelle könnte man sich auch überlegen sich vom Socket-Objekt ein Dateiobjekt geben zu lassen. Da bekommt man die jeweils nächste Zeile mit `next()`.

Die Methode ist auch nicht robust. Wenn der Sender kein Zeilenende am Ende der Daten schickt, dann hängt die Methode.

``'%s' % obj`` ist ziemlich sinnfrei wenn `obj` vom Typ `str` ist.

Für Debug-Ausgaben bietet sich das `logging`-Modul an. Für Logausgaben sowieso.

Die Implementierung der GET-Methode hat das Problem, dass Du gar keine weiteren Header von der GET-Anfrage an den Webserver weiterleitest *bevor* Du die Antwort liest. Also eigentlich kannst Du `urlopen()` an der Stelle gar nicht einfach so verwenden, sondern müsstest das selbst implementieren.

Dateien die man öffnet sollte man auch wieder schliessen. Die Sprache garantiert nicht, dass die Objekte zeitnah zerstört werden wenn sie nicht mehr erreichbar sind und Dateideskriptoren sind ein verhältnismässig knappes Gut.

`readlines()`/`writelines()` ist hier auch eine unpassende Wahl denn es kann sich ja auch um Binärdaten handeln wie Bilder, wo es gar keine Zeilen gibt. Ausserdem ist alles auf einmal zu lesen problematisch. Denn spätestens wenn jemand ein DVD-Image lädt, bekommt man Probleme mit dem Arbeitsspeicher und er vielleicht sogar eine Zeitüberschreitung vom Browser weil der ja solange nichts vom Proxy hört bis der das Image komplett in den Speicher geladen hat.

Vom Pfad einfach ohne Erklärung die ersten sieben Zeichen abzuschneiden ist ziemlich „magisch”.

Für das Parsen von URLs gibt es in `urllib`/`urllib2` fertige Funktionen. Also zum Beispiel um Host und Port zu erfahren. Dann braucht man nicht immer dieses unschöne `find()`. Du müsstest auch mal probieren ob Dein Ansatz so mit Authentifikation klarkommt wenn jemand eine URL im Format 'http://user:password@domain/foo/bar' angibt.

Pfadkomponenten sollten mit `os.path.join()` zusammengesetzt werden. Der Pfad der dort zusammengesetzt und gegebenfalls erstellt wird, findet im weiteren dann aber gar keine Verwendung.

Vergleiche mit literalen `True` oder `False` sind schlechter Stil weil überflüssig. Da kommt doch bloss wieder ein Wahrheitswert bei heraus. Ob etwas Wahr ist kann man am Wert selber schon festmachen und wenn man das Gegenteil testen möchte, negiert man einfach mit ``not``.
Sebi.Schneider
User
Beiträge: 38
Registriert: Freitag 3. Mai 2013, 15:05

@BlackJack

super vielen Dank für deine Mühe, werd mich mal ran setzen
Antworten