GabrielBiga
GabrielBiga

Reputation: 398

Simple webserver in C. fgets causes infinite loop to read browser request

I trying make one simple HTTP server in C, but i need get the browser requests to see platform, cookies, ... To do that, i'm trying read the sock file with the fgets function, but it returns to me an infinite loop.

See my code.

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdbool.h>

#define SERVER          "TestServer4"               //Servername
#define PROTOCOL        "HTTP/1.1"                  //Protocol used
#define RFC1123FMT      "%a, %d %b %Y %H:%M:%S GMT" //Date format of returns
#define PORT            7777                        //Socket port
#define NUM_THREADS     5                           //Threads clusters numbers

int sock; //Stores the socket

void send_headers(FILE *f, int status, char *title, char *extra, char *mime, int length, time_t date) {
    time_t now;
    char timebuf[128];

    fprintf(f, "%s %d %s\r\n", PROTOCOL, status, title);
    fprintf(f, "Server: %s\r\n", SERVER);
    now = time(NULL);
    strftime(timebuf, sizeof (timebuf), RFC1123FMT, gmtime(&now));
    fprintf(f, "Date: %s\r\n", timebuf);
    if (extra) fprintf(f, "%s\r\n", extra);
    if (mime) fprintf(f, "Content-Type: %s\r\n", mime);
    if (length >= 0) fprintf(f, "Content-Length: %d\r\n", length);
    if (date != -1) {
        strftime(timebuf, sizeof (timebuf), RFC1123FMT, gmtime(&date));
        fprintf(f, "Last-Modified: %s\r\n", timebuf);
    }
    fprintf(f, "Connection: close\r\n");
    fprintf(f, "\r\n");
}

void send_error(FILE *f, int status, char *title, char *extra, char *text) {
    send_headers(f, status, title, extra, "text/html", -1, -1);
    fprintf(f, "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\r\n", status, title);
    fprintf(f, "<BODY><H4>%d %s</H4>\r\n", status, title);
    fprintf(f, "%s\r\n", text);
    fprintf(f, "</BODY></HTML>\r\n");
}

void get_request(FILE *f) {
    char buf[4096];

    while (fgets(buf, sizeof(buf), f) != NULL) {        
        fgets(buf, sizeof(buf), f);

        printf("%s", buf);
    }

}

int process(FILE *f, long tid) {
    char buf[4096];
    char *method;
    char *path;
    char *protocol;

    struct stat statbuf;  

    if (!fgets(buf, sizeof(buf), f))  return -1;

    get_request(f);

    method = strtok(buf, " ");
    path = strtok(NULL, " ");
    protocol = strtok(NULL, "\r");
    if (!method || !path || !protocol) return -1;

    fseek(f, 0, SEEK_CUR); // Force change of stream direction

    if (strcasecmp(method, "GET") != 0) {
        send_error(f, 501, "Not supported", NULL, "Method is not supported.");
    } else {             
        send_headers(f, 200, "OK", NULL, "text/html", -1, statbuf.st_mtime);

        bool page = !strcmp(path, "/photos.json");

        if (page) {
           fprintf(f, "<b> yeah, photos stream :) </b>"); 
        }  else {           
           fprintf(f, "<HTML><HEAD><TITLE>Yeah</TITLE></HEAD>\r\n<BODY>");
           fprintf(f, "<H4>server (%s) </H4>\r\n<PRE>\n", path); 
        }         
    }

    printf("Im running in thread #%ld!\n", tid); 
    fclose(f);
    return 0;
}

void accept_conn(void *threadid) {
    long tid;
    tid = (long) threadid;

    while (1) {
        int s;
        FILE *f;        

        s = accept(sock, NULL, NULL);
        if (s < 0) { 
            break;
        } else {                    
            f = fdopen(s, "a+");   
            process(f, tid);                   
        };
    }
}

int main(int argc, char *argv[]) {

    struct sockaddr_in sin;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(PORT);
    bind(sock, (struct sockaddr *) &sin, sizeof (sin));

    listen(sock, 5);
    printf("HTTP server listening on port %d\n", PORT);

    pthread_t threads[NUM_THREADS];
    int rc;
    long t;
    for (t = 0; t < NUM_THREADS; t++) {
        printf("In main: creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, accept_conn, (void *) t);
        if (rc) {
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }

    /* Last thing that main() should do */
    pthread_exit(NULL);

    close(sock);
    return 0;
}

The problem is in the get_request function.

Upvotes: 2

Views: 1730

Answers (1)

Arun Taylor
Arun Taylor

Reputation: 1572

What is happening is that fgets blocks after it has finished reading the end of the http request, i.e. "\r\n". You are reading from a socket, not a file.

What you need to do is something like this. Also, the buffer and its length has to be passed in so that you can do something with the buffer later.

void
get_request(FILE *f, char *buf, int len)
{
    int n;
    char *p = buf;

    while (1) {
        fgets(p, len, f);

        if (strcmp(p, "\r\n") == 0) {
            break;
        }

        n = strlen(p);
        p += n;
        len -= n;
    }

    printf("req = %s\n", buf);
}

Upvotes: 1

Related Questions