HTTP Request - Wo zuende?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
tobias.hartwich
User
Beiträge: 6
Registriert: Donnerstag 16. März 2006, 08:22

Donnerstag 16. März 2006, 08:27

Hallo,

Ich moechte mit Sockets(!) einen Http-Request absetzen und die komplette Antwort erhalten. Da die Antwort ueber mehrere Pakete verteilt sein koennte, muss ich mehrfach empfangen. Das ist solange kein Problem solange mir der zustaendige Webserver einen Content-Laenge sendet. Viele Webserver tun dies leider nicht. Gibt es eine zuverlaessige Moeglichkeit herauszufinden wann eine Antwort zu Ende ist, außer einem Timeout?

Mein bisheriges Skript sieht wiefolgt aus:

Code: Alles auswählen

import socket, re

REQUEST = "GET / HTTP/1.1  \r\n \
Host: web.de\r\n\r\n"

class HTTPCON:
    def __init__(self):
        self.__meinsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__meinsocket.connect( ( "web.de", 80 ) )

        self.__meinsocket.send(REQUEST)
        chunk = ""
        i = 0
        while True:
            chunk_temp = self.__meinsocket.recv(1024)
            print chunk_temp
            if i == 0:
                content_split = chunk_temp.split("\r\n\r\n")
                header = content_split[0]
                reguexpre = "Content-Length: ([0-9]+)"
                re_objekt = re.compile(reguexpre)
                content_laenge =  int(re_objekt.findall(header)[0])

                for content_element in content_split:
                    if content_element != header:
                        chunk += content_element

            else:
                chunk += chunk_temp
            if len(chunk) >= content_laenge: break

            i += 1

        filehandle = file("bla.txt", "wb")
        filehandle.write(chunk)
        filehandle.close()
        print chunk

HTTPCON()


Gruß Tobi
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Donnerstag 16. März 2006, 08:44

Du musst Dich auf jeden Fall mit dem HTTP Protokoll befassen.
Beim kurzen Überfliegen des Codes erkenne ich nicht, dass Du
zuerst den Header erfasst und anschliessend die Body Daten.

Wichtig zu wissen ist, dass der Header mit "\r\n" getrennt wird...

Code: Alles auswählen

HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 7

Hello Tobi...

Tabellar :wink:
Benutzeravatar
jens
Moderator
Beiträge: 8482
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Donnerstag 16. März 2006, 08:46

Es dürfte so ähnlich gehen, wie ein file-Objekt auslesen... Solange, bis keine Daten mehr kommen...

Also ungefähr so:

Code: Alles auswählen

while 1:
    data = conn.recv(1024)
    if not data: break
    ...
EDIT: Ich würde Content-Length auch ehr mit .split(":") und int() auswerten und nicht mit re... Aber das nur nebenbei...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
tobias.hartwich
User
Beiträge: 6
Registriert: Donnerstag 16. März 2006, 08:22

Donnerstag 16. März 2006, 09:09

Hallo, danke fuer die Antworten:
tabellar hat geschrieben:Du musst Dich auf jeden Fall mit dem HTTP Protokoll befassen.
Beim kurzen Überfliegen des Codes erkenne ich nicht, dass Du
zuerst den Header erfasst und anschliessend die Body Daten.

Wichtig zu wissen ist, dass der Header mit "\r\n" getrennt wird...

Code: Alles auswählen

HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 7

Hello Tobi...

Tabellar :wink:
Jepp, das habe ich gemacht. Ich trenne header und Daten und versuche die Content-Length auszulesen. Das Problem ist, dass diese nicht immer gesendet wird vom Webserver
jens hat geschrieben:Es dürfte so ähnlich gehen, wie ein file-Objekt auslesen... Solange, bis keine Daten mehr kommen...

Also ungefähr so:

Code: Alles auswählen

while 1:
    data = conn.recv(1024)
    if not data: break
    ...
Ja, Den Ansatz hatte ich auch, das Problem ist, dass sobald alle Daten da sind recv trotzdem weiterlaeuft und weiterhin versucht daten zu bekommen. Dies tut recv solange bis wirklich wieder was ueber die Leitung kommt. Man koennte das natuerlich ueber einen Timeout loesen aber das ist sehr unsauber, wie ich finde

Gruß Tobi
Benutzeravatar
jens
Moderator
Beiträge: 8482
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Donnerstag 16. März 2006, 09:14

tobias.hartwich hat geschrieben:Ja, Den Ansatz hatte ich auch, das Problem ist, dass sobald alle Daten da sind recv trotzdem weiterlaeuft und weiterhin versucht daten zu bekommen.
Stimmt, ich erinnere mich, als ich die CVS anbindung geschrieben hab: http://www.python-forum.de/viewtopic.php?p=23853#23853

Es muß eigentlich irgendein "ENDE"-Zeichen kommen, bei dem man weiß, das die Seite vollständig ist...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Moderator
Beiträge: 8482
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Donnerstag 16. März 2006, 09:19

Hm. Ich hab es gerade mal getestet... Bei mir geht's:

Code: Alles auswählen

import socket

#~ host = "www.google.de"
host = "www.heise.de"
port = 80
url = "/"

print "Connecte zu %s:%s..." % (host, port),
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
print "OK"

request = "GET %s HTTP/1.0\r\n\r\n" % url
#~ request = "GET %s HTTP/1.1\r\n\r\n" % url
print "Send Request '%s'..." % request.encode("String_Escape"),
s.send(request)
print "OK"

print "Read data..."
print "-"*79
while 1:
    data = s.recv(1024)
    if not data:
        break
    print data

print "-"*79
print "OK"
s.close()
Kann es sein, das am Ende zwei mal "\n\r" gesendet werden?

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
tobias.hartwich
User
Beiträge: 6
Registriert: Donnerstag 16. März 2006, 08:22

Donnerstag 16. März 2006, 09:20

Genau nach diesem Ende-Zeichen suche ich ;)

Habe noch folgendes gefunden:
*
Fragmentierte Nachrichten:

HTTP-Nachrichten können einen Rumpf beliebiger Länge enthalten, so daß ein Mechanismus definiert werden muß, um das Ende einer Nachricht zu bestimmmen. HTTP/1.0 stellt dafür den Content-Length Header zur Verfügung, der die Länge des Nachrichtenrumpfes enthält. Dazu muß aber offensichtlich die Länge der Entität bereits im voraus bekannt sein, was bei dynamisch generierten Entitäten nicht der Fall ist. Bisher mußten solche Entitäten vollständig generiert und gepuffert werden, um ihre Länge zu berechnen und konnten erst dann verschickt werden, wodurch die Antwortzeit (bei großen Antworten deutlich) anwächst. Bei HTTP/1.0 ist dies noch kein bedenkliches Problem gewesen, da der Content-Length Header nicht notwendig gewesen ist, das Ende der Nachricht ist ja durch das Schließen der Verbindung markiert worden. In HTTP/1.1 ist dies nicht mehr sinnvoll, daher ist eine bessere Möglichkeit entwickelt worden, die fragmentierte Transferkodierung (Chunked Transfer-Encoding):
Der Sender zerlegt die Nachricht in Fragmente beliebiger Länge, die mit ihrer Länge verschickt werden, wobei ein leeres Fragment das Ende der Nachricht bezeichnet. Der Sender markiert eine fragmentierte Nachricht durch den Header Transfer-Encoding: chunked.
Dadurch kann der Sender jeweils nur kleine Teile der Nachricht puffern, ohne daß merklich Komplexität oder Overhead hinzugefügt wird. Aufgrund der Einfachheit dieser Lösung müssen alle HTTP/1.1-Implementationen dieses Verfahren unterstützen.
Quelle: http://www.informatik.uni-frankfurt.de/~mahmoud/

Gruß, Tobi
tobias.hartwich
User
Beiträge: 6
Registriert: Donnerstag 16. März 2006, 08:22

Donnerstag 16. März 2006, 09:29

Danke, deine Loesung funktioniert bei mir auch - Aber leider nicht fuer alle Seiten. Hab es jetzt noch einmal getestet mit quasi deiner Loesung aber bei mir versucht er am Ende weiterhin zu empfangen - genau da liegt das Problem

Code: Alles auswählen

import socket, re

REQUEST = "GET / HTTP/1.1  \r\n Host: lesty.de\r\n\r\n"

class HTTPCON:
    def __init__(self):
        self.__meinsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__meinsocket.connect( ( "lesty.de", 80 ) )

        self.__meinsocket.send(REQUEST)
        chunk = ""
        while 1:
            data = self.__meinsocket.recv(1024)
            if not data:
                break
            print data
            chunk += data


        print chunk

        self.__meinsocket.close()


HTTPCON()
//EDIT: Hab es gerade nochmal mit HTTP 1.0 getestet. ich weiß nicht warum - aber damit funktioniert es?!
//EDIT2: Hab rausgefunden, dass ein Vorteil von HTTP 1.1 ist, dass persistente Verbindungen aufgebaut werden. Die muss man natuerlich auch schließen :-) - also Es klappt nun - Danke

Gruß, Tobi
Benutzeravatar
jens
Moderator
Beiträge: 8482
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Donnerstag 16. März 2006, 09:47

Du könntest auch mal nachsehen, wie es die HTTPLIB macht ( http://svn.python.org/view/python/trunk ... iew=markup ) Das nutzt IMHO http 1.1...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten