http.cpp




#include <iostream.h>
#include <string.h>
#include <errno.h>
#include "http.hpp"



// for timeout management
// UNIX: Set a dummy timeout for SIGALRM (otherwise the signal
// aborts the application).

#if OS == UNIX_OS
        #include <signal.h>
        static void handler (int signo) { }
#endif

// -------------------------------------------------------------
// system dependent functions
// -------------------------------------------------------------

// -------------------------------------------------------------

void Http::set_timer() throw (Http_timer_error)
{
        #if OS == UNIX_OS
                struct sigaction new_sigalarm;

                new_sigalarm.sa_handler = handler;

                if (sigemptyset (&new_sigalarm.sa_mask) == -1)
                { throw Http_timer_error(); }

                new_sigalarm.sa_flags = 0;

                if (sigaction (SIGALRM, &new_sigalarm, NULL) == -1)
                { throw Http_timer_error(); }
        #endif

#if 0
        #if OS == WINDOWS_OS
        if (timeout > 0)
        {
                int cr;
                cr = setsockopt (
                                                        sock,
                                                        IPPROTO_TCP,
                                                        SO_RCVTIMEO,
                                                        (char FAR *)&timeout,
                                                        sizeof(int)
                                                );
                if (cr == SOCKET_ERROR) { throw Http_timer_error(); }
        }
        #endif
#endif

}

// -------------------------------------------------------------

void Http::set_alarm()
{
        #if OS == UNIX_OS
        if (timeout > 0) { alarm (timeout); }
        #endif
}

// -------------------------------------------------------------

void Http::reset_alarm()
{
        #if OS == UNIX_OS
        if (timeout > 0) { alarm (0); }
        #endif
}

// -------------------------------------------------------------

Http::Http()
{
        port = -1;
        timeout = -1;
        hostname = NULL;
        url = NULL;
        out_file = NULL;
        buffer = NULL;
        buffer_size = 0;
}

// -------------------------------------------------------------

Http::Http(char* mhostname, char* mulr, int mport, char* mout_file, int mtimeout) throw (Http_memory_error)
{
        set_port(mport);
        set_hostname(mhostname);
        set_url(mulr);
        if (mout_file != NULL) { set_out_file (mout_file); }
        set_timeout(mtimeout);

        buffer_size = 0;
}

// -------------------------------------------------------------

Http::~Http()
{
        if (hostname != NULL) { delete [] hostname; }
        if (url != NULL) { delete [] url; }
        if (out_file != NULL) { delete [] out_file; }
        if (buffer != NULL) { delete [] buffer; }
}

// -------------------------------------------------------------

void Http::re_alloc_buffer() throw (Http_memory_error)
{
        char* aux = new char[buffer_size];
        if (aux == NULL) { throw Http_memory_error(); }
        
        memcpy (aux, buffer, buffer_size);

        delete [] buffer;
        buffer_size *= 2;
        buffer = new char[buffer_size];
        if (buffer == NULL) { delete [] aux; throw Http_memory_error(); }
        memcpy (buffer, aux, buffer_size/2);

        delete [] aux;
}

// -------------------------------------------------------------

void Http::set_port(int mport)
{ port = mport; }

// -------------------------------------------------------------

void Http::set_timeout(const int mtimeout)
{ timeout = mtimeout; }

// -------------------------------------------------------------

void Http::set_hostname(const char *mhostname) throw (Http_memory_error)
{
        if (hostname != NULL) { delete [] hostname; }
        hostname = new char[strlen(mhostname)+1];
        if (hostname == NULL) { throw Http_memory_error(); }
        strncpy (hostname, mhostname, strlen(mhostname)+1);
}

// -------------------------------------------------------------

void Http::set_url (const char *mulr) throw (Http_memory_error)
{
        if (url != NULL) { delete [] url; }
        url = new char[strlen(mulr)+1];
        if (url == NULL) { throw Http_memory_error(); }
        strncpy (url, mulr, strlen(mulr)+1);
}

// -------------------------------------------------------------

void Http::set_out_file (const char *mout_file) throw (Http_memory_error)
{
        if (out_file != NULL) { delete [] out_file; }
        out_file = new char[strlen(mout_file)+1];
        if (out_file == NULL) { throw Http_memory_error(); }
        strncpy (out_file, mout_file, strlen(mout_file)+1);
}

// -------------------------------------------------------------

void Http::get() throw (Http_memory_error, Http_invalid_args, Http_connect_failed, Http_write_failed, Http_read_failed, Http_timer_error, Http_timeout_connect, Http_timeout_read)
{
        int cr;


        // -----------------------------------------------------
        // Make sure that we don't have stupid args
        if (
                (hostname == NULL)
                ¦¦
                (url == NULL)
                ¦¦
                (port == -1)
         ) { throw Http_invalid_args(); }

        // -----------------------------------------------------
        // Set timers (UNIX: signal SIGALRM)
        // This can throw the exception "Http_timer_error")

        #if OS == UNIX_OS
        set_timer();
        #endif

        // -----------------------------------------------------
        // Try to open the connexion with the server
        // Note that "set_alarm()" and "reset_alarm()" are used
        // to produce a timeout under UNIX. To do that we use
        // signals ... UNIX signals are not supported under Win-
        // -dows, For Windows the "connect timeout" is imple-
        // -mented using non blocking sockets.

        set_alarm();
        sock = open_tcp_connexion (&server_address, hostname, port, timeout);
        reset_alarm();





        if (
                (sock == SCK_CREATE_ERROR)
                ¦¦
                (sock == SCK_CONNECT_ERROR)
                ¦¦
                (sock == SCK_GET_IP_ERROR)
                ¦¦
                (sock == SCK_SET_NON_BLOCK_ERR)
                ¦¦
                (sock == SCK_SET_BLOCK_ERR)
         ) { throw Http_connect_failed(sock); }

        if (sock == SCK_TIMEOUT_CONNECT) { throw Http_timeout_connect(); }

        #if OS == WINDOWS_OS
        set_timer();
        #endif

        // -----------------------------------------------------

        // "GET url".
        // Please note that "GET " is 4 characters long.
        // Please nothe that " HTTP/1.0" is 9 characters long.
        // We alse add 2 "cariage return" at the end of the URL.
        // A "cariage return" is 2 characters long "\012\015" (in octal)
        // <=> characters 10 and 13 in decimal.

        int request_size = strlen("GET ") +
                                                strlen(url) +
                                                strlen(" HTTP/1.0") +
                                                strlen("\nUser-Agent: ApacheBench") +
                                                strlen("\nHost: ") +
                                                strlen(hostname) +
                                                strlen("\nAccept: */*") +
                                                strlen("\012\015\012\015") +
                                                1;

        
        char *request = new char[request_size];
        if (request == NULL)
        {
                CLOSE_SOCKET(sock);
                throw Http_memory_error();
        }

        request[0] = 0;
        strcat (request, "GET ");
        strcat (request, url);
        strcat (request, " HTTP/1.0");
        strcat (request, "\nUser-Agent: ApacheBench");
        strcat (request, "\nHost: ");
        strcat (request, hostname);
        strcat (request, "\nAccept: */*");
        strcat (request, "\012\015\012\015");
        request [request_size-1] = 0;

        // -----------------------------------------------------

        cr = WRITE_TO_SOCKET(sock, request, request_size, 0);
        if (cr != request_size)
        {
                delete [] request;
                CLOSE_SOCKET(sock);
                throw Http_write_failed();
        }
        delete [] request;

        // -----------------------------------------------------

        // Now get the data
        if (buffer != NULL) { delete [] buffer; buffer_size = 0; }
        buffer = new char[BUFFER_INIT_SIZE];
        if (buffer == NULL)
        {
                CLOSE_SOCKET(sock);
                throw Http_memory_error();
        }
        buffer_size = BUFFER_INIT_SIZE;

        int total_lu = 0;
        char *pt = buffer;
        int a_lire = buffer_size - total_lu - 1;

        #if OS == UNIX_OS
        int err;
        #endif

        set_alarm();
        cr = READ_FROM_SOCKET(sock, pt, a_lire, 0);
        reset_alarm();

        #if OS == UNIX_OS
        err = errno;
        #endif

        if (cr == SCK_ERROR_READ)
        {
                delete [] buffer;
                buffer = NULL;
                buffer_size = -1;
                CLOSE_SOCKET(sock);

                #if OS == UNIX_OS
                if (err == EINTR) { throw Http_timeout_read(); }
                #endif

                throw Http_read_failed();
        }

        pt[cr] = 0;
        total_lu += cr;

        // keep in mind that 'buffer_size' is modified by 're_alloc_buffer()'

        // while (cr == a_lire)
        while (cr > 0)
        {

                // -------------------------------------------------
                // We realloc memory only if we need to ... The read
                // from a socket does not necessarily return the
                // requested number of bytes.
                // -------------------------------------------------

                if (total_lu >= buffer_size - 1)
                {
                        try { re_alloc_buffer(); }
                        catch ( Http_memory_error e )
                        {
                                buffer_size = -1;
                                CLOSE_SOCKET(sock);
                                throw Http_memory_error();
                        }
                }

                pt = buffer + total_lu;
                a_lire = buffer_size - total_lu - 1;

                set_alarm();
                cr = READ_FROM_SOCKET(sock, pt, a_lire, 0);
                reset_alarm();



                #if OS == UNIX_OS
                err = errno;
                #endif

                if (cr == SCK_ERROR_READ)
                {
                        delete [] buffer;
                        buffer = NULL;
                        buffer_size = -1;
                        CLOSE_SOCKET(sock);

                        #if OS == UNIX_OS
                        if (err == EINTR) { throw Http_timeout_read(); }
                        #endif

                        throw Http_read_failed();
                }


                if (cr == 0) { break; }
                total_lu += cr;
                
                buffer[total_lu] = 0;
        }
        buffer_size = total_lu;

        
        CLOSE_SOCKET(sock);


}

// -------------------------------------------------------------

int Http::get_header(char **dest)
{
        if (buffer == NULL) { *dest = NULL; return -1; }

        // Apache separates the header from the body with the 4 following charaters
        // "\012\015\012\015"
        // Microsoft server separates the header from the body with the 4 following charaters
        // "\015\012\015\012"

        char *pt;

        // Does it comes from a Microsoft's server ?
        pt = strstr (buffer, "\015\012\015\012");

        if (pt == NULL)
        {
                // Does it come from Apache ?
                pt = strstr (buffer, "\012\015\012\015");
                if (pt == NULL) { *dest = NULL; return -1; }
        }

        char aux = *pt;
        *pt = 0;

        int size = strlen(buffer);

        *dest = new char[size+1];
        if (*dest == NULL) { *pt = aux; *dest=NULL; return -1; }
        strncpy(*dest, buffer, size);
        (*dest)[size]=0;
        *pt = aux;
        return size;
}

// -------------------------------------------------------------

int Http::get_status(char **dest)
{
        if (buffer == NULL) { *dest = NULL; return -1; }

        char *start = strstr (buffer, "HTTP");
        if (start == NULL) { *dest = NULL; return -1; }
        char *stop = strstr (buffer, "\012");
        if (stop == NULL) { *dest = NULL; return -1; }
         
        char aux = *stop;
        *stop = 0;

        int size = strlen(start);

        *dest = new char[size+1];
        if (*dest == NULL) { *stop = aux; *dest=NULL; return -1; }
        strncpy(*dest, start, size);
        (*dest)[size]=0;
        *stop = aux;
        return size;
}