Problem mit boost asio TCP iostream
-
Hallo.
Diesen Code habe ich heute nachmittag niedergetippt um eine Datei mit dem HTTP-Protokoll zu laden:
size_t DownloaderBase::downloadFile_( const string& httpRequest, ofstream& output) { //Ensure that the connection is opened properly if(!connection_.good()) throw runtime_error("Cant download file, bad connection"); //Send http request to establish download connection_ << httpRequest.c_str() << flush; //Fetch HTTP-Header first HttpHeader header; for(string current(""); current != "\r"; getline(connection_, current)) header.applyLine(current); //If the server signals error, stop if(header.statusCode != 200) throw runtime_error("Cant download file, server signaled error"); //8kb buffer should be sufficient to minimize overhead const size_t bufferSize = 8192; boost::array<char, bufferSize> buffer; const size_t fileSize = header.contentLength; size_t readBytes = 0; while(connection_.good() && output.good()) { //We need to do this check to avoid appending junk to a //file from previous reads if(fileSize > bufferSize + readBytes) { connection_.read(&buffer[0], bufferSize); output.write(&buffer[0], bufferSize); readBytes += bufferSize; } else { const size_t reqSize = fileSize - readBytes; connection_.read(&buffer[0], reqSize); output.write(&buffer[0], reqSize); readBytes += reqSize; } } //Verificate read bytes if(readBytes != fileSize) throw runtime_error("Error occurred downloading file"); return readBytes; }Allerdings will das so mal nicht.
Ich habe testweise versucht die Index-Datei von google.com zu laden und habe mir zu Testzwecken die Zeilen ausgeben lassen anstatt sie in meine HttpHeader-Klasse zu speisen. Resultat: Es fing irgendwo bei den Cookies an, der komplette Rest war weggeschnitten.Woran könnte das liegen? Bin gerade etwas ratlos.
Danke schon mal

Grüße,
Flo
-
Es kann tausend Gründe haben. Vielleicht gibst du die Zeilen vorher nicht aus?
Letztendlich sind zu viele unbekannte Klassen/Funktionen (und damit zu viel unbekannter Code) im Spiel, um das Problem in angemessener Zeit ausmachen zu können. Es gleicht einem Ratespiel.
Ansonsten gibt es einen Artikel zum Thema HTTP im Magazin. Vielleicht findest du dort etwas hilfreiches.
-
Ok, hier ein Minimalbeispiel. Exakt das gleiche Ergebnis:
#include <iostream> #include <boost/asio.hpp> using namespace std; int main() { boost::asio::ip::tcp::iostream stream("www.google.com", "http"); stream << "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n" << flush; for(string current(""); current != "\r"; getline(stream, current)) cout << current; }Output:
Set-Cookie: PREF=ID=84367f46456aedfb:FF=0:TM=1288118722:LM=1288118722:S=H3l_NRz-IkGnQXD4; exSet-Cookie: NID=40=CeFvdYJN5Cu_1h5L48ti5KB5in5XX-P0vdTSBmMpj5ggD-AoS1uDkl_3kQdzauF7OL3a1cOd7tiBEngcyXFLz3F6ufYUTVfplct5K0rU7PQXigrwUrb5qd2x4m1SuZxF; expires=Wed, 27-Apr-2011 18:45:22 G
Erwarteter Output:
HTTP/1.1 302 Found Location: http://www.google.de/ Cache-Control: private Content-Type: text/html; charset=UTF-8 Set-Cookie: PREF=ID=4f32d5b91c45647e:FF=0:TM=1288118796:LM=1288118796:S=4zgIcy6oGUHEpwnP; expires=Thu, 25-Oct-2012 18:46:36 GMT; path=/; domain=.google.com Set-Cookie: NID=40=Yy047GoUFZ1MCDqNlcXuPcb8vYYCUdmXzrbj3NARh7280OSh1KHAC6J_U5pfvjmgUTJZbgO9Dbh0npSXdTB9gG8qyLMMhEfopBKrLOyHXqKPxmt-fTbAF_b2msTZcCoF; expires=Wed, 27-Apr-2011 18:46:36 GMT; path=/; domain=.google.com; HttpOnly Date: Tue, 26 Oct 2010 18:46:36 GMT Server: gws Content-Length: 218 X-XSS-Protection: 1; mode=block
-
Das Problem ist äusserst witzig. Wenn ich recht habe, liegt das Problem nicht an Asio. Es liegt an der Konsole

Füge nach der Ausgabe von
currentauf der Konsole noch ein<< '\n'dazu. Also schlussendlich:#include <string> // <- braucht es eigentlich auch noch... #include <iostream> #include <boost/asio.hpp> using namespace std; int main() { boost::asio::ip::tcp::iostream stream("www.google.com", "http"); stream << "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n" << flush; for(string current(""); current != "\r"; getline(stream, current)) cout << current << '\n'; }Und dann staune ab meinen magischen Zauberkräften.
Meine Vermutung lautet:
Es wird eine Zeile ausgelesen, am Ende wird das '\n' von der Zeile entfernt, aber '\r' bleibt drin. Auf der Windowskonsole führt das '\r' dazu, dass das Caret wieder auf den Anfang der Zeile geht. Halt eben ein Wagenrücklauf wie bei den alten Schreibmaschinen. Danach wird die gleiche Zeile einfach wieder neu überschrieben.
Grüssli
-
Upps, darauf wäre ich wohl nicht gekommen, danke

Der Download funktioniert immer noch nicht, denke aber dass der Bug irgendwo im HttpHeader-Parser ist.

-
Was ist den connection_.read()? Laut der Doku zur Version 1.44 gibt es entweder eine freie Funktion read(), die dann den socket als ersten Parameter nimmt, oder eine member Funktion receive(), die dann aber mit weniger gelesenen bytes zurück kehren kann. Ich würde in jedem Fall die Zahl der gelesenen Bytes auswerten.
Anderer Grund könnte chunked encoding sein. Versuch mal den request mit der Version 1.0 zu senden.
mfg Torsten
-
Was ist den connection_.read()?
Die asio iostreams sind voll kompatible STL-streams, von daher wird read() von std::istream geerbt.
