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


  • Administrator

    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 current auf 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. 🙂


Anmelden zum Antworten