Binmode STDOUT



  • Und die Antwort auf @_ro_ro ist: geht nicht in Standard-C++, sondern ist "implementation dependent".

    Siehe https://isocpp.org/wiki/faq/input-output#binary-mode-for-cin-cout

    Hier hat jemand dasselbe gefragt: https://stackoverflow.com/questions/5654067/how-to-make-cout-behave-as-in-binary-mode und die Lösung ist im Prinzip die, die du oben schon nutzt.



  • @_ro_ro sagte in Binmode STDOUT:

    Mein CGI/c++ entwickle ich auf Windows 10

    Ah, erste Zeile mitlesen hilft.

    Dein "Production System" ist aber ein Linux, richtig? Ist zwar nicht die Lösung des geschilderten Problems, aber ich würde es dann wohl erstmal mit dem Windows Subsystem for Linux probieren um auf einer produktionsnahen Umgebung zu entwickeln.

    Ich habe auch mal kurz nach CGI Libraries gegoogelt, die scheinen vor allem FastCGI zu implementieren und damit das Problem zu umgehen.



  • @Schlangenmensch

    CGI's entwickeln heißt plattformunabhängig entwickeln. Es ist also egal ob man das auf Win32 oder Sun/OS oder Linux tut. Produktionsnah heißt nur, daß man in seiner Entwicklungsumgebung dasselbe Ergebnis bekommt. So sehe ich das und diese Haltung haben alle Unternehmen unterstützt für die ich gearbeitet habe. Was ja auch gar nicht anders umgesetzt werden kann. So habe ich Perl/CGI's entwickelt die auf Sun/Solaris, auf FreeBSD, Debian, IBM AIX und Win32 laufen mussten. Was nicht zuletzt auch einem heterogenem Campus-Netzwerk geschuldet war.

    MFG



  • @_ro_ro Mit nativen Programmiersprachen ist "Crossplattform" aber nicht unbedingt trivial. Es gibt Dinge, die sind OS abhängig, ist gibt Dinge die sind eventuell Compiler Abhängig und so weiter. Du hast jetzt eine Macro Überprüfung ob das ganze auf Windows oder Linux läuft und scheinst damit das korrekte Verhalten zu erziehlen. Was passiert, wenn du das auf einem anderen System kompilierst, weißt du aber nicht.
    Ich habe mich mit Perl nicht intensiv beschäftigt, ich würde aber vermuten, dass dir da viel der Interpreter abnimmt.

    Produktionsnah bedeutet für mich, dass sich die Systeme ähneln, insbesondere was Compiler und Betriebssystem betrifft, und ich nicht auf Entwicklungs und Produktionsumgebung unterschiedlichen Code ausführe damit es da nicht zu unerwarteten Überraschungen kommt.



  • @Schlangenmensch

    ja sicher doch. Wenn man auf einem anderen System compiliert weiß man nie was passiert 😉
    Und bei CGI/1.1 kommt noch hinzu wie der Webserver HTTP implementiert, insbesondere beim Parsen der Header. Da gibt es auch noch Unterschiede, Beispiel:

    Normalerweise parst der Webserver die Header die er von einem CGI-Prozess bekommt und toleriert Groß/Kleinschreibung. Kommt da ein content-type: text/html korrigiert der Apache das zu Content-Type... aber mir sind auch schon Webserver untergekommen die das nicht tun und einen fehlenden Content-Type konstatieren um dann mit Status 500 Interner Server Error die Ohren anzulegen.
    Ebenso setzen fast alle Webserver ein einfaches "\n" (LF) um zu einem "\r\n" CRLF. Und dann gibt es auc Webserver denen kann man beliebige Requestmethoden wie "ZITRONE" unterjubeln und auch bei einem "GET" einen Messagebody ohne Content-Type senden.

    Da gibt es also Einiges zu prüfen um unliebsamen Überraschungen zuvorzukommen 😉

    MFG

    PS; Diese binmode-Issues können auch vom Apache Win32 her kommen!!! Wenn ich mal viel Zeit habe untersuche ich das genauer.



  • header fields und values sind per rfc case-insensitive. jeder webserver der dir 500 wirft weil die header lower-case sind, kann einmal direkt in the muell.



  • @Cardiac sagte in Binmode STDOUT:

    header fields und values sind per rfc case-insensitive. jeder webserver der dir 500 wirft weil die header lower-case sind, kann einmal direkt in the muell.

    Ja. Es gib eben Dinge die man einfach mal so hinnehmen muss weil sie so sind 😉

    MFG



  • @_ro_ro

    um es vorweg zu nehmen: Das Problem liegt nicht in c++. Ein binmode STDOUT löst jedoch das Problem was ganz woanders liegt. Doch wo genau liegt denn nun das Problem?
    Ich habe aufgrund eines Hinweis nun einen Hexdump erstellt. Dazu pipe ich die Ausgabe eines c++Program auf ein Perlscript, also über den Weg stdout/stdin, untenstehend die c++ Source und Perl zum Nachvollziehen:

    int main(){
        for( unsigned char c = 0; c < 256; c++){
                cout << c;
        }
        return 0;
    }
    
    while ( read(STDIN, my $c, 1) ){
        printf "%02X\n", unpack "C", $c;
    }
    

    Augeführt so fw | c:\perl64\bin\perl.exe hexdump.pl | more > dump.txt und siehe da, von 0x00 bis 0xFF ist alles angekommen. C++ gibt also die Binary korrekt aus.

    Das heißt, daß der issue STDOUT auf Win32 zwischen dem CGI-Prozess und dem ApacheWin32 zustandekommt. In dieser Versuchsanordnung ändert sich nichtsam Ergebnis, wenn ich im c++Programm binmode STDOUT einschalte. Was da genau auf dem Common Gateway (CGI) passiert, entzieht sich leider meiner Kenntnis. Entscheidend jedoch ist, daß es eine Lösung gibt.

    Viele Grüße!!



  • @_ro_ro sagte in Binmode STDOUT:

    for( unsigned char c = 0; c < 256; c++){

    Preisfrage: wann bricht diese Schleife ab?



  • @wob

    sorry, falschen Code gestellt. Hier der Richtige

        for( int i = 0; i < 256; i++){
            unsigned char c = i;
            cout << c;
        }
    

    Am Ergebnis ändert das nix 😉

    mfg



  • @_ro_ro sagte in Binmode STDOUT:
    Du kannst dir die temporäre Variable sparen:

    std::cout << static_cast<unsigned char>( i );
    


  • @DocShoe sagte in Binmode STDOUT:

    @_ro_ro sagte in Binmode STDOUT:
    Du kannst dir die temporäre Variable sparen:

    std::cout << static_cast<unsigned char>( i );
    

    Oh danke, ja werd' ich bestimmt noch öfter brauchen 😉
    Viele Grüße!!!

    PS: Heute Lötkolben statt Tastaur. Pünktlich zum Beginn der Fahrradsaison eben reingekommen:
    PC Schaltnetzteil 12 V / 100 W
    Damit läuft der Kompressor (der lag noch irgendwo rum). Damit Luftaufpumpen wieder Spaß macht 😉



  • @DocShoe

    btw, bisher habe ich

    // Percent Encodings
        unsigned char hex(string &hex){
            unsigned char c;
            try{
                size_t pos{};
                c = stoi(hex, &pos, 16);
            }
            catch(std::invalid_argument const& ex){
                throw string("stoi invalid argument");
            }
            catch(std::out_of_range const& ex){
                throw string("stoi ot of range");
            }
            return c;
        };
    

    was mir auch nicht so richtig gefällt. Aber der Cast zu unsigned char funktioniert. Verbessern oder so stehen lassen?

    Viele Grüße.



  • @_ro_ro
    Eine Exception zu fangen und dann als std::string neu zu werfen ist nicht gut. Wenn du nicht explizit jeden Exceptiontyp fangen einzeln willst kannst du einfach std::exception fangen, weil alle STL Exceptiontypen davon erben. Mit std::exception::what() kommst du dann an die Fehlermeldung.
    Eigentlich brauchst du die ganze Funktion nicht, denn wenn du hex aufrufst musst du Exceptions fangen, dann kannst du auch direkt std::stoul aufrufen. Der zweite Parameter ist optional, wenn du den nicht auswerten möchtest kannst du auch nullptr übergeben.

    unsigned char c = std::stoul( hex, nullptr, 16 );
    

    Überspitzt gesagt macht deine Funktion genau das Gleiche wie std::stoul, nur dass sie eine schlechtere Fehlerbehandlung hat.



  • Die Frage ist aber, was passieren soll, wenn im Hexstring mehr als 2 Ziffern vorhanden sind, also soll z.B. "ABCD" als 0xCD zurückgegeben werden oder aber eine Exception werfen?



  • @Th69 sagte in Binmode STDOUT:

    Die Frage ist aber, was passieren soll, wenn im Hexstring mehr als 2 Ziffern vorhanden sind, also soll z.B. "ABCD" als 0xCD zurückgegeben werden oder aber eine Exception werfen?

    Gute Frage 😉
    Jedoch kommt dieser Fall gar nicht vor. Die Prozentkodierung kodiert als %ff genau ein Byte. Wenn der Parser ein Prozentzeichen findet, schickt er genau 2 Zeichen zum Decoder, nämlich die 2 Zeichen die rechts neben dem Prozentzeichen stehen.

    Und wie ich schon einmal schrieb, beim Provider läuft der Apache mit mod_security. Wenn da ein %ZZ im Request ankommt, wird das mit Status 400 quittiert. Es ist nur so, daß ich diesen Status auf Perl umlege per .htaccess und damit die Exception von C++ gar nicht zu sehen bekomme, weil die Antwort von Perl kommt.

    Lokal jedoch sehe ich die Exception, weil ich lokal kein mod_security installiert habe.

    MFG



  • @_ro_ro sagte in Binmode STDOUT:

    Und wie ich schon einmal schrieb, beim Provider läuft der Apache mit mod_security. Wenn da ein %ZZ im Request ankommt, wird das mit Status 400 quittiert. Es ist nur so, daß ich diesen Status auf Perl umlege per .htaccess und damit die Exception von C++ gar nicht zu sehen bekomme, weil die Antwort von Perl kommt.

    Eine Anmerkung zu dem Gedankengang: Damit koppelst du dein Programm natürlich in gewisser Weise an Apache mit mod_security und die .htaccess-Konfiguration, bzw. an ein vergleichbares Setup und reduzierst eventuell die Portabilität (hier: auf andere Webserver). Das kann man so machen, ich persönlich bevorzuge allerdings, wenn möglichst wenig Vorbedingungen erfüllt sein müssen, damit mein Programm korrekt funktioniert. Selbst wenn in dem Fall doppelt geprüft würde.



  • @Finnegan

    ja sicher doch braucht es eine eigene Apache-Konfiguration. Also eine .htaccess und die ist lokal wie remote dieselbe. Ob mod_security läuft oder nicht spielt in meiner Software überhaupt gar keine Rolle. Der Webserver muß nur wissen welche URLs nach Perl, PHP oder C++ geroutet werden und das regelt die Dateierweiterung .html | .chtml | .phtml

    MFG



  • @_ro_ro
    Ich denke, du hast @Th69 und @Finnegan da nicht richtig verstanden. Im Optimalfall sollte man Software wiederverwendbar machen, damit man das Rad nicht x-mal neu erfinden muss. In deinem Fall wird hex nur aufgerufen, wenn der string genau zwei Zeichen lang ist und nur gültige Hex-Ziffern enthält, weil der Webserver das sicherstellt. In einem anderen Kontext (anderes Projekt, anderer Webserver, was auch immer) ist das vielleicht nicht mehr der Fall, und dann funktioniert's halt nicht mehr. Wenn man das beim Schreiben der Funktion schon beachtet erlebt man später weniger Überraschungen.



  • @DocShoe sagte in Binmode STDOUT:

    @_ro_ro
    Ich denke, du hast @Th69 und @Finnegan da nicht richtig verstanden. Im Optimalfall sollte man Software wiederverwendbar machen, damit man das Rad nicht x-mal neu erfinden muss. In deinem Fall wird hex nur aufgerufen, wenn der string genau zwei Zeichen lang ist

    Stimmt. Von daher habe ich das auch geändert damit es evntl. woanders wiederverwendet werden kann:

        int hex(string &hex){
            int i;
            try{
                i = stoi(hex, nullptr, 16);
            }
            catch(std::invalid_argument const& ex){
                throw string("stoi invalid argument");
            }
            catch(std::out_of_range const& ex){
                throw string("stoi ot of range");
            }
            return i;
        };
    

    Und der Cast genau da wo er gebraucht wird:

        // in Funktion percent_decode
        tokens << static_cast<unsigned char>( my::hex(hex) );
    

    Danke nochmal und Viele Grüße!


Anmelden zum Antworten