doria90
doria90

Reputation: 45

C written HTTP server resets connection after repling

I've read all the related answers but could not find the reason why this is happening. I've tried replacing close() to shutdown and then close and more but still the same. All I need is to be able to browse to my server and when asking a file it'll return its content and when asking a directory it will return a list of whats in it. this all WORKS, except for the RST packet. Can anyone help?

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>

#define HTTP_BAD_REQUEST "HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>File Not Found</TITLE>\n</HEAD>\n<BODY>\n<br/>\n</BODY>\n</HTML>\n"
#define HTTP_GOOD_REQUEST "HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>Results</TITLE>\n</HEAD>\n<BODY>\n"
#define HTTP_ERROR "HTTP/1.1 500 Internal Server Error\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>Internal Server Error!</TITLE>\n</HEAD>\n<BODY>\n<br/>\n</BODY>\n</HTML>\n"
#define HTTP_NOT_IMPLEMENTED "HTTP/1.1 501 Not Implemented\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>Method Not Implemented</TITLE>\n</HEAD>\n<BODY>\n<br/>\n</BODY>\n</HTML>\n"
#define HTTP_END "<br/></BODY></HTML>\n\0"
#define MAX_REQUEST_SIZE 1024


int send_response(int fd, char* filepath)
{
    struct stat statbuf;

    int iofd;
    int check = 1;
    char buf[1024];

    if ( stat(filepath,&statbuf) < 0 )
    {
    printf("Path not found\n");
    send(fd, HTTP_BAD_REQUEST, strlen(HTTP_BAD_REQUEST), 0);

        return -1;
    }
    else
    {
        if(S_ISDIR(statbuf.st_mode))
        {
            //directory
            DIR *dp;
            struct dirent *ep;

            dp = opendir (filepath);
            if (dp == NULL)
            {
        printf("Error opening directory\n");
        send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
                return -1;
            }

        send(fd, HTTP_GOOD_REQUEST, strlen(HTTP_GOOD_REQUEST), 0);

        ep = readdir (dp);
            while (ep != NULL)
            {
        send(fd, ep->d_name, strlen(ep->d_name), 0);
        send(fd, "<br/>", strlen("<br/>"), 0);
        ep = readdir (dp);
            }

            send(fd, HTTP_END, strlen(HTTP_END), 0);

            (void) closedir (dp);
            return 0;
        }
        else if (S_ISREG(statbuf.st_mode))
        {
            //regular file

            iofd = open(filepath, O_RDONLY);
            if( iofd < 0 )
            {
                printf("Error opening file\n");
        send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
                return -1;
            }

        send(fd, HTTP_GOOD_REQUEST, strlen(HTTP_GOOD_REQUEST), 0);

            while ( check > 0)
            {
                check = read(iofd,buf,sizeof(buf));
                if (check < 0)
                {
                    printf("Error reading file\n");
            send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
                    close(iofd);

                    return -1;
                }
                else if (check == 0)
            break;
                else
            send(fd, buf, strlen(buf), 0);
            }

            send(fd, HTTP_END, strlen(HTTP_END), 0);

        close(iofd);
            return 0;
        }
    }
}

int process(int fd, char* header)
{
    char npath[MAX_REQUEST_SIZE];

    char *eol = strchr(header, '\r');

    // split header to get path and method
    char *method = strtok(header, " ");
    char *path = strtok(NULL, " ");
    char *http = strtok(NULL, " ");
    if( eol != NULL )
        *eol = '\0';


    if( strcmp(method, "GET") || (strcmp(method, "POST")))
    {
        if( path[0] == '/' && path[1] == '\0' )
        {
            path ="/";
        }
        else if( path[0] == '/' )
        {
            snprintf(npath, MAX_REQUEST_SIZE, "%s", path);
            path = npath;
        }

        return send_response(fd, path);
    }
    else
    {
        send(fd, HTTP_NOT_IMPLEMENTED, strlen(HTTP_NOT_IMPLEMENTED), 0);
        return -1;
    }
}

int get_line(int sock, char *buf, int size)
{
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(sock, &c, 1, 0);

        if (n > 0)
        {
            if (c == '\r')
            {
                n = recv(sock, &c, 1, MSG_PEEK);
                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }
            buf[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    buf[i] = '\0';

    return(i);
}

int service(int fd)
{
    char buffer[MAX_REQUEST_SIZE];

    if (get_line(fd, buffer, MAX_REQUEST_SIZE) <= 0)
    {
        printf("Error reading from socket");
        send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
        return -1;
    }
    return process(fd, buffer);
}
void cleanup( int signal )
{

    int pid;
        while(1)
        {
            pid = waitpid(-1, NULL, WNOHANG);

            if( pid < 0 )
                break;
            else if( pid == 0 )
                break;
        }
    exit(0);
}
int main(int argc, char *argv[])
{
    int port = 80;
    int max_requests;
    struct sigaction finish;

    finish.sa_handler = &cleanup;
    sigaction( SIGINT, &finish, NULL );

    if ((argc == 1) || (argc > 3))
    {
        printf("Usage Error: wrong amount of arguments to main");
        return -1;
    }
    else if (argc == 3)
    {
        max_requests = strtol(argv[1], NULL, 0);
        port = strtol(argv[2], NULL, 0);
    }
    else
        max_requests = strtol(argv[1], NULL, 0);


    struct sockaddr_in servaddr;
    int serversock = socket(AF_INET, SOCK_STREAM, 0);
    int on = 1;


    if( serversock < 0 )
    {
        printf("Error creating socket: %s\n", strerror(errno));
        return 1;
    }


    if( setsockopt(serversock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0 )
        printf("Error tweaking socket options: %s\n", strerror(errno));

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(port);
    if( bind(serversock, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )
    {
        printf("Error binding to server address: %s\n", strerror(errno));
        return 1;
    }

    if( listen(serversock, max_requests) < 0 )
    {
        printf("Error using listen(): %s\n", strerror(errno));
        return 1;
    }

    while(1)
    {
        int clientsock, pid, childstatus;

        //server shall now wait for a new connection
        clientsock = accept(serversock, NULL, NULL);
        if( clientsock < 0 )
        {
            printf("Error using accept(): %s\n", strerror(errno));
            return 1;
        }

        pid = fork();
        if( pid < 0 )
        {
            printf("Error using fork(): %s\n", strerror(errno));
            send(clientsock, HTTP_ERROR, strlen(HTTP_ERROR), 0);
            close(clientsock);
            continue;
        }
        else if( pid == 0 )
        {
            shutdown(serversock, 2);
            if (service(clientsock) != 0 )
        printf("error in service\n");
        if (shutdown(clientsock, SHUT_WR) != 0)
        {
        printf("Error shutting the socket down: %s\n", strerror(errno));
        return -1;
        }
        close(clientsock);  
            return 0;
        }

        pid = waitpid(-1, NULL, WNOHANG);
        if( pid < 0 )
        {
                printf("Error using waitpid(): %s\n", strerror(errno));
                break;
        }
    close(clientsock);

    }
    return 999;
}

Upvotes: 0

Views: 81

Answers (1)

user3629249
user3629249

Reputation: 16540

the following code:

  1. cleanly compiles
  2. properly outputs error messages to stderr
  3. properly outputs a 'usage' message when not correct number of command line parameters
  4. 'should' operate correctly with connections after the first

caveat: not thoroughly tested.

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>

#define HTTP_BAD_REQUEST "HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>File Not Found</TITLE>\n</HEAD>\n<BODY>\n<br/>\n</BODY>\n</HTML>\n"
#define HTTP_GOOD_REQUEST "HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>Results</TITLE>\n</HEAD>\n<BODY>\n"
#define HTTP_ERROR "HTTP/1.1 500 Internal Server Error\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>Internal Server Error!</TITLE>\n</HEAD>\n<BODY>\n<br/>\n</BODY>\n</HTML>\n"
#define HTTP_NOT_IMPLEMENTED "HTTP/1.1 501 Not Implemented\nContent-Type: text/html\nConnection: Closed\n\n<HTML>\n<HEAD>\n<TITLE>Method Not Implemented</TITLE>\n</HEAD>\n<BODY>\n<br/>\n</BODY>\n</HTML>\n"
#define HTTP_END "<br/></BODY></HTML>\n\0"

#define MAX_REQUEST_SIZE 1024


int send_response(int fd, char* filepath)
{
    struct stat statbuf;

    int     iofd;    // file descriptor
    ssize_t readStatus = 1;
    char    buf[ MAX_REQUEST_SIZE ];

    if ( stat(filepath,&statbuf) < 0 )
    {
        perror( "stat for file failed" );
        //printf("Path not found\n");
        send(fd, HTTP_BAD_REQUEST, strlen(HTTP_BAD_REQUEST), 0);
        return -1;
    }

    else
    { // path found
        if(S_ISDIR(statbuf.st_mode))
        {
            //directory
            DIR *dp;
            struct dirent *ep;

            dp = opendir (filepath);
            if (dp == NULL)
            {
                fprintf( stderr, "Error opening directory\n");
                send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
                return -1;
            }

            // implied else, opendir successful

            send(fd, HTTP_GOOD_REQUEST, strlen(HTTP_GOOD_REQUEST), 0);

            ep = readdir (dp);

            while (ep != NULL)
            {
                send(fd, ep->d_name, strlen(ep->d_name), 0);
                send(fd, "<br />", strlen("<br />"), 0);
                ep = readdir (dp);
            }

            send(fd, HTTP_END, strlen(HTTP_END), 0);

            closedir (dp);
            return 0;
        }

        else if (S_ISREG(statbuf.st_mode))
        { //regular file
            iofd = open(filepath, O_RDONLY);
            if( iofd < 0 )
            {
                //fprintf(stderr, "Error opening file\n");
                perror( "open failed" );
                send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
                return -1;
            }

            // implied else, open successful

            send(fd, HTTP_GOOD_REQUEST, strlen(HTTP_GOOD_REQUEST), 0);

            while ( readStatus > 0)
            {
                readStatus = read(iofd,buf,sizeof(buf));
                if (readStatus < 0)
                {
                    perror( "read of file failed");
                    //printf("Error reading file\n");
                    send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
                    close(iofd);
                    return -1;
                }

                else if (readStatus == 0) // EOF or signal
                    break;

                else // transmit line from file
                    send(fd, buf, strlen(buf), 0);
            } // end while

            send(fd, HTTP_END, strlen(HTTP_END), 0);

            close(iofd);
            return 0;
        }
    }
    return -2;
}


int process(int fd, char* header)
{
    // split header to get path and method
    char *method = strtok(header, " ");
    char *path = strtok(NULL, " ");
    //char *http = strtok(NULL, " ");

    // if trailing newline, replace with NUL byte
    char *eol = NULL;
    if( NULL == (eol = strchr(header, '\r') ) )
        *eol = '\0';


    if( strcmp(method, "GET") || (strcmp(method, "POST")))
    {
        return send_response(fd, path);
    }

    else
    {
        send(fd, HTTP_NOT_IMPLEMENTED, strlen(HTTP_NOT_IMPLEMENTED), 0);
        return -1;
    }
} // end function: process


int get_line(int sock, char *buf, int size)
{
    int     i = 0;
    char    c = '\0';
    ssize_t recvStatus;

    while ( (i < (size - 1)) && (c != '\n') )
    {
        recvStatus = recv(sock, &c, 1, 0);

        if ( recvStatus > 0)
        { // then some bytes read
            if (c == '\r')
            {
                recvStatus = recv(sock, &c, 1, MSG_PEEK);

                if ((recvStatus > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);

                else
                    c = '\n';
            } // endif

            buf[i] = c;
            i++;
        }

        else
        {
            c = '\n';
        } // endif
    } // end while

    // terminate the char array
    buf[i] = '\0';

    return(i);
} // end function: get_line


int service(int fd)
{
    char buffer[MAX_REQUEST_SIZE];

    if (get_line(fd, buffer, MAX_REQUEST_SIZE) <= 0)
    {
        fprintf( stderr, "Error reading from socket");
        send(fd, HTTP_ERROR, strlen(HTTP_ERROR), 0);
        return -1;
    }

    return process(fd, buffer);
} // end function: service


void cleanup( int signal )
{
    (void)signal;
    pid_t pid;

    while(1)
    {
        pid = waitpid(-1, NULL, WNOHANG);

        if( pid <= 0 )
            break;
    }
    exit(0);
} // end function: cleanup


int main(int argc, char *argv[])
{
    uint16_t port = 80;
    int      max_requests;
    struct sigaction finish;

    finish.sa_handler = &cleanup;
    sigaction( SIGINT, &finish, NULL );

    if ( (argc != 3) && (argc != 2) )
    {
        fprintf( stderr, "USAGE: %s <maxConnections> [<portToUse>]\n", argv[0]);
        //printf("Usage Error: wrong amount of arguments to main");
        return -1;
    }


    max_requests = (int)strtol(argv[1], NULL, 0);

    if( 3 == argc )
    {
        port = (uint16_t)strtol(argv[2], NULL, 0);
    }

    int serversock = socket(AF_INET, SOCK_STREAM, 0);
    if( serversock < 0 )
    {
        printf("Error creating socket: %s\n", strerror(errno));
        return 1;
    }

    int on = 1;
    if( setsockopt(serversock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0 )
    {
        perror( "setsockopt for SO_REUSEADDR failed" );
        //printf("Error tweaking socket options: %s\n", strerror(errno));
    }

    if( setsockopt(serversock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0 )
    {
        perror( "setsockopt for SO_KEEPALIVE failed" );
        //printf("Error tweaking socket options: %s\n", strerror(errno));
    }

    struct sockaddr_in servaddr;

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(port);

    if( bind(serversock, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )
    {
        perror( "bind failed" );
        //printf("Error binding to server address: %s\n", strerror(errno));
        return 1;
    }

    if( listen(serversock, max_requests) < 0 )
    {
        perror( "listen failed" );
        //printf("Error using listen(): %s\n", strerror(errno));
        return 1;
    }

    while(1)
    {
        int clientsock;
        pid_t pid;
        //int childstatus;

        //server shall now wait for a new connection
        clientsock = accept(serversock, NULL, NULL);
        if( clientsock < 0 )
        {
            perror( "accept failed" );
            //printf("Error using accept(): %s\n", strerror(errno));
            close( serversock );
            return 1;
        }

        pid = fork();
        switch( pid )
        {
            case -1: // fork failed
                fprintf(stderr, "Error using fork(): %s\n", strerror(errno));
                send(clientsock, HTTP_ERROR, strlen(HTTP_ERROR), 0);
                close(clientsock);
                break;

            case 0: // in child process
                //shutdown(serversock, 2);

                if (service(clientsock) != 0 )
                {
                    fprintf( stderr, "error in service\n");
                }

                close(clientsock);
                return 0;
                break;

            default: // in parent process
                pid = waitpid( -1, NULL, WNOHANG);
                close(clientsock);
                break;
        } // end switch
    } // end while
    return 0;
} // end function: main

Upvotes: 1

Related Questions