https chunks senden

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Boa
User
Beiträge: 190
Registriert: Sonntag 25. Januar 2009, 12:34

Hi,

Wie sende ich eine Datei nach und nach per HTTPS?
Ich scheitere schon dabei, dass ich HTTPS verwenden möchte, anstatt HTTP. Falls ich dazu chunked encoding brauche muss ich vermutlich die größe der chunks angeben. Muss ich auch die line-endings ("\r\n") extra senden, oder passiert das automatisch?

Code: Alles auswählen

   conn = httplib.HTTPConnection(str("https://"+self.host))
        conn.connect()
        conn.putrequest('PUT', "/file/:sc:%s:%s/data" % (self.uid, path_to_file))
        #conn.putheader('Transfer-Encoding', 'chunked')
        conn.putheader("Authorization", self.token)
        conn.putheader('Connection', 'Keep-Alive')
        conn.putheader('Cache-Control', 'no-cache')
        conn.endheaders()
        while True:
            data = str(fileobject.read(10*1000*1000))
            conn.send(data)
            if len(data) < 10*1000*1000:
                break
BlackJack

@Boa: '\r\n' wird bei `send()` nicht automatisch dazwischen geschoben. Die Methode kann doch auch gar nicht entscheiden *wann* sie das tun müsste.

Du müsstest neben dem Header der sagt, dass die Daten chunked sind vor jedem Block die Länge des Blocks in Bytes auf einer Zeile, also abgeschlossen mit '\r\n', senden, und nach dem letzten Block noch eine 0. Da würde es sich anbieten die Schleife und die Abbruchbedingung entsprechend anzupassen, also nicht abbrechen wenn `read()` weniger als die Blockgrösse geliefert hat, sondern wenn es *keine* Daten mehr geliefert hat. Damit würde man es sich ersparen die Blockgrösse dort zweimal hinzuschreiben, was in der Form eine Verletzung des DRY-Prinzips darstellt.

Nur zur Sicherheit: Du kannst die Grösse vom `fileobject` nicht im voraus bestimmen? Oder das `requests`-Modul würde die Sache nicht eventuell vereinfachen? Siehe http://docs.python-requests.org/en/late ... d-requests
Boa
User
Beiträge: 190
Registriert: Sonntag 25. Januar 2009, 12:34

Hallo,

Doch, die Dateigröße ist bekannt. Die requests Methode habe ich übersehen, vermutlich weil ich nach put & chunks gesucht hatte und nicht nach post. Ich habe versucht nachzuvollziehen, was die put Methode macht. Zuerst ruft die Methode die request Methode auf, die sehr allgemein gehalten ist und auch für die anderen Anfragen (POST, GET) verwendet wird. request() nimmt aber angeblich schon keine Daten in Form von Dateiobjekten an, ich glaube aber, das stimmt nicht. requests() erstellt ein Request Objekt aus dem Modul .models. Das verarbeitet das Dateiobjekt vermutlich weiter. Schließlich kommt man in das Modul adapters mit der send Methode, die bei bekannter Größe das hier macht:

Code: Alles auswählen

                for i in request.body:
                    low_conn.send(hex(len(i))[2:].encode('utf-8'))
                    low_conn.send(b'\r\n')
                    low_conn.send(i)
                    low_conn.send(b'\r\n')
                low_conn.send(b'0\r\n\r\n')
So wie ich es verstehe: Für jeden "chunk", sende die Länge+Zeilenumbruch gefolgt von aktuellem chunk counter+Zeilenumbruch. Schließlich wird das ende des HTTP Requests gesendet. Die 0 hatte ich total vergessen. Ist schon ewig her, dass ich mir das RFC durchgelesen habe :)
Jetzt bleibt zu hoffen, dass Request die Datei nicht ganz einliest, sondern request.body irgendeine Art Generator ist. Ich probiere das Mal aus und sonst versuche ich das mit httplib umzusetzen.

Danke für den Tipp :)
BlackJack

@Boa: Wenn die Dateigrösse bekannt ist, dann müsste man doch eigentlich nicht chunked senden‽ Oder verlangt das der Empfänger aus irgendwelchen esoterischen Gründen?

`request()` kann als `data` auch direkt ein Dateiobjekt entgegennehmen. Und wenn man in `requests.PreparedRequest.prepare_body()` nachliest, sollte das eigentlich automatisch zu 'chunked' führen, weil es eine `__iter__()`-Methode hat und weder Zeichenkette noch Wörterbuch ist, noch eine Länge mit `len()` bestimmt werden kann. Also so etwas wie ``requests.put('url...', data=fileobj, stream=True)`` könnte zum Erfolg führen.
Boa
User
Beiträge: 190
Registriert: Sonntag 25. Januar 2009, 12:34

Es geht in erster Linie darum weniger Arbeitsspeicher zu beanspruchen. Chunked encoding muss nicht sein, außer vielleicht, wenn ich später die Möglichkeit implementieren will den upload wieder aufnehmen zu können.
Boa
User
Beiträge: 190
Registriert: Sonntag 25. Januar 2009, 12:34

Folgendes scheint nicht zu funktionieren, bzw. recht viel Speicher zu brauchen:

Code: Alles auswählen

requests.put(url, data=fileobject, headers=headers)
Dabei wird die Datei zwar nach und nach gelesen, aber im Endeffekt hat das Modul trotzdem die gesamte Datei im Buffer.
Boa
User
Beiträge: 190
Registriert: Sonntag 25. Januar 2009, 12:34

@BlackJack: Stimmt. Ich versuche das Problem zu isolieren. Vermutlich liegt es in meinem Code.
Antworten