Gehe zu deutscher Webseite

ViaThinkSoft CodeLib

This article is in:
CodeLibProgramming aidsC / C++

Verworfener Code. Ersetzt durch LibCURL Variante.

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // sockaddr_in
#include <netdb.h> // gethostbyname(), hostent
#include <signal.h>
#include <stdexcept>

//********************************************************************************************
//
// http_callUrl
//
// Teilt eine URL (Form: http://www.example.com/dir/script.ext?querystring) in ihre Bestandteile und übergibt diese an http_call

std::runtime_error CreateSocketError()
{
    std::ostringstream temp;
#ifdef linux
    temp << "Socket-Fehler #" << errno << ": " << strerror(errno);
#else
    int error = WSAGetLastError();
    temp << "Socket-Fehler #" << error;
    char* msg;
    if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                     NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                     reinterpret_cast<char*>(&msg), 0, NULL))
    {
        try
        {
            temp << ": " << msg;
            LocalFree(msg);
        }
        catch(...)
        {
            LocalFree(msg);
            throw;
        }
    }
#endif
    return std::runtime_error(temp.str());
}

// Liest eine Zeile des Sockets in einen stringstream
void GetLine(int socket, std::stringstream& line)
{
    for(char c; recv(socket, &c, 1, 0) > 0; line << c)
    {
        if(c == '\n')
        {
            return;
        }
    }
    throw CreateSocketError();
}

// TODO: Wird HTTP:// und http:// berücksichtigt? (protokoll ist case insensitive)

bool http_callUrl (string url) {
        logEvent(VERBOSE_LEVEL_DEBUG, "callURL: " + url);

        string server = "";
        unsigned int port;
        bool use_ssl;
        string request = "GET / HTTP/1.1";

        // http:// suchen und löschen
        // find_first_of() war falsch. dort wurde nur nach EINEM der zeichen gesucht, nicht nach der kombination
        if (url.find("http://") == 0) {
                url = url.substr(7, url.length() - 7);
                use_ssl = false;
                port = 80; // Standard-Port
        } else if (url.find("https://") == 0) {
                url = url.substr(8, url.length() - 8);
                use_ssl = true;
                port = 443; // Standard-Port
        } else {
                logEvent(VERBOSE_LEVEL_ERROR, "Invalid URL - Protocol unknown or not given: " + url);
                return false;
        }

        // Slash finden und String splitten
        int slashPos = url.find_first_of("/");
        if (slashPos >= 0) {
                server = url.substr(0, slashPos);
                request = "GET " + url.substr(slashPos, url.length() - slashPos) + " HTTP/1.1";
        } else
                server = url;

        // Doppelpunkt finden und String splitten
        int colonPos = server.find_first_of(":");
        if (colonPos >= 0) {
                port = StrToInt(server.substr(colonPos + 1, server.length() - colonPos - 1));
                server = url.substr(0, colonPos);
        }

        if (server == "") {
                logEvent(VERBOSE_LEVEL_WARNING, "No server address given.");
                return false;
        } else {
                request += "\r\n";
                request += "Host: ";
                request += server;
        }

        request += "\r\n";
        request += "User-Agent: ";
        request += USER_AGENT;

        request += "\r\n";
        request += "Connection: close";

        request += "\r\n\r\n";

        logEvent(VERBOSE_LEVEL_DEBUG, "callURL: goto http_call with SERVER = " + server + " PORT = " + IntToStr(port) + " REQUEST = " + request);

        return http_call(server, port, request, use_ssl);
}

//********************************************************************************************
//
// http_call
//
// Ruft eine HTTP-Webseite auf und liefert den Erfolgswert zurück

// TODO: Fehlercodes beachten, nicht nur Socketfehler berücksichtigen
// TODO: Umleitungen beachten (absolute und relative 30x-Adressangaben)
// TODO: Korrekt mit SSL umgehen
// TODO: User-Agent an Server senden
// TODO: Ausgabe korrekt parsen und zurückgeben

bool http_call(string server, unsigned int port, string request, bool use_ssl) {
        logEvent(VERBOSE_LEVEL_DEBUG, "Call URL: http://" + server + ":" + IntToStr(port) + "/ - REQUEST: " + request);

        // Establish connection to server
        hostent* phe = gethostbyname(server.c_str());

        if(phe == NULL) {
                logEvent(VERBOSE_LEVEL_WARNING, "Could not resolve hostname " + server + "!");
                return false;
        }

        if(phe->h_addrtype != AF_INET) {
                logEvent(VERBOSE_LEVEL_ERROR, "Invalid address type!");
                return false;
        }

        if(phe->h_length != 4) {
                logEvent(VERBOSE_LEVEL_ERROR, "Invalid address type!");
                return false;
        }

        // TODO: Auch SSL-Sockets unterstützen!! (Pflicht beim WebBase-Secure-Mode)

        int Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(Socket == -1) {
                logEvent(VERBOSE_LEVEL_ERROR, "Could not establish socket!");
                return false;
        }
        sockaddr_in service;
        service.sin_family = AF_INET;
        service.sin_port = htons(port); // Das HTTP-Protokoll benutzt Port 80

        char** p = phe->h_addr_list; // p mit erstem Listenelement initialisieren
        int result; // Ergebnis von connect
        do
        {
                if(*p == NULL) // Ende der Liste
                {
                        logEvent(VERBOSE_LEVEL_ERROR, "Could not establish connection to server " + server + "!");
                        return false;
                }

                service.sin_addr.s_addr = *reinterpret_cast<unsigned long*>(*p);
                p++;
                result = connect(Socket, reinterpret_cast<sockaddr*>(&service), sizeof(service));
                logEvent(VERBOSE_LEVEL_DEBUG, "Server " + server + ": New connection try - result = " + IntToStr(result));
        }
        while(result == -1);

        logEvent(VERBOSE_LEVEL_DEBUG, "Connection to server " + server + " on port " + IntToStr(port) + " established!");

        // Send HTTP request
        int bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
        do
        {
                int result = send(Socket, request.c_str() + bytesSent, request.size() - bytesSent, 0);
                logEvent(VERBOSE_LEVEL_DEBUG, "Server " + server + ": HTTP result = " + IntToStr(result));
                if (result < 0) { // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
                        logEvent(VERBOSE_LEVEL_ERROR, "Socket error #" + IntToStr(errno) + ": " + strerror(errno));
                        return false;
                }
                bytesSent += result;
        } while(bytesSent < request.size());

        // Return true

        while(true)
        {
                stringstream line;
                try
                {
                        GetLine(Socket, line);
                }
                catch(exception& e) // Ein Fehler oder Verbindungsabbruch
                {
                        break; // Schleife verlassen
                }
                logEvent(VERBOSE_LEVEL_DEBUG, "Ausgabe: " + line.str());
        }

        logEvent(VERBOSE_LEVEL_DEBUG, "HTTP call successful!");
        close(Socket);

        return true;
}
Daniel Marschall
ViaThinkSoft Co-Founder