Question

I have been working on creating a small C server to which browsers can connect and make "GET" requests for files. I ran the basic structure of the server using the typical "socket", "bind", "listen", and "accept". Then I used the file descriptor "accept" in order to invoke "write" or "send" calls to the client, which makes "GET" requests in the form of

    http://ip_address:port/request

The first step I took was reading from the accept_fd (returned by accept) and get the first line of it, which was "GET /request HTTP/1.1\r\n". Then I use this to create a proper HTTP headers to send files over.

    GET /image_file.gif HTTP/1.1\r\n
    \r\n
    HTTP/1.1 200 OK\r\n
    Content-Type: image.gif\r\n
    Content-Length: 9184\r\n
    \r\n

The content length was fetched from the st_size of the file statistics and corresponds to the file I want to send over, so this tells me I am dealing with the right file. I use "fopen" with "r" for reading and store the content with "fread" into a big character array which I made. Then finally, I sent the buffer in which the content of the file is stored using "send" and the client browser receives it, but the contents displayed are some gibberish string of unrecognizable characters (possibly binary).

    GIF89awÄ��ëQiò‹›òŒ›øÅÍìRiæ&Cýðòîn‚õ¨´éC\úÓÙè4Pûâæó™§í`uð}Žö¶Àõ©

Here is a snippet of what it looks like on the browser instead of an image file. I have been trying to solve this problem for four days and tried various things like sending each part of the HTTP headers individually instead of sending it all at once, sending the image file one byte at a time using "fgetc" and "write" but none of these attempts succeeded. Is there something wrong with what I am doing to send an image file over? (and the html files too. when I try to send it over, all the tags appear as though it was a text file instead of the browser interpreting and formatting the page.)

#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define STAT_200 " 200 OK\r\n"
#define STAT_404 " 404 Not Found\r\n"
#define STAT_501 " 501 Not Implemented\r\n"

#define F_DIR "Content-Type: text/directory\r\n"
#define F_GIF "Content-Type: image/gif\r\n"
#define F_HTML "Content-Type: text/html\r\n"
#define F_JPEG "Content-Type: image/jpeg\r\n"
#define F_JPG "Content-Type: image/jpg\r\n"
#define F_TXT "Content-Type: text/plain\r\n"

typedef enum {cgi, gif, html, jpeg, jpg, plain} ext;

ext get_ext(char *file) {
    if (strstr(file, ".cgi") != NULL)
        return cgi;
    if (strstr(file, ".gif") != NULL)
        return gif;
    if (strstr(file, ".html") != NULL)
        return html;
    if (strstr(file, ".jpeg") != NULL)
        return jpeg;
    if (strstr(file, ".jpg") != NULL)
        return jpg;
    if (strstr(file, ".txt") != NULL)
        return plain;
}

void parse(int accept_fd) {
    char *response = (char *) malloc(sizeof(char) * 512);
    char *content = (char *) malloc(sizeof(char) * 512);
    if (read(accept_fd, content, 512) < 0) {
        perror("read error");
        exit(1);
    }

    char *part_end = strstr(content, "\n");
    *(part_end + 1) = 0;     // still has \r\n
    strcat(response, content);
    strcat(response, "\r\n");
    // send(accept_fd, content, strlen(content), MSG_CONFIRM);
    // send(accept_fd, "\r\n", 2, MSG_CONFIRM);     // empty line
    *(part_end - 1) = 0;     // no more \r\n

    char *type = (char *) malloc(sizeof(char) * 256);      // "GET"
    char *request = (char *) malloc(sizeof(char) * 256);   // "/request"
    char *version = (char *) malloc(sizeof(char) * 256);   // "HTTP/x.x"
    strcpy(type, content);
    strcpy(request, strstr(content, "/"));
    strcpy(version, strstr(content, "HTTP"));
    part_end = strstr(type, "/");
    *(part_end - 1) = 0;
    part_end = strstr(request, "HTTP");
    *(part_end - 1) = 0;

    strcat(response, version);
    // send(accept_fd, version, strlen(version), MSG_CONFIRM);     // write the "HTTP/x.x"

    if (strcmp(type, "GET") != 0) { // 501
        strcat(response, STAT_501);
        // send(accept_fd, STAT_501, strlen(STAT_501), MSG_CONFIRM);

    }

    struct stat f_stat;
    int stat_fd;
    char *cwd = (char *) malloc(sizeof(char) * 256);
    char *f_name = (char *) malloc(sizeof(char) * 256);
    if ((cwd = getcwd(cwd, 256)) == NULL) {
        perror("getcwd error");
        exit(1);
    }
    strcpy(f_name, cwd);
    strcat(f_name, request);

    if ((stat_fd = stat(f_name, &f_stat)) < 0) { // 404
        strcat(response, STAT_404);
        // send(accept_fd, STAT_404, strlen(STAT_404), MSG_CONFIRM);

    }

    if S_ISDIR(f_stat.st_mode) { // 200
        strcat(response, STAT_200);
        strcat(response, F_DIR);
        strcat(response, "\r\n"); 
        // send(accept_fd, STAT_200, strlen(STAT_200), MSG_CONFIRM);    // # stat
        // send(accept_fd, "Content-Type: text/directory\r\n", 30, MSG_CONFIRM);
        // send(accept_fd, "\r\n", 2, MSG_CONFIRM);
        write(accept_fd, response, strlen(response));

        int red;
        if ((red = dup2(accept_fd, STDOUT_FILENO)) < 0) {
            perror("dup error");
            exit(1);
        }
        if (execlp("ls", "ls", f_name, NULL) < 0) {
            perror("exec error");
            exit(1);
        }

        close(accept_fd);
        exit(0);
    } else if S_ISREG(f_stat.st_mode) { // 200
        strcat(response, STAT_200);
        // send(accept_fd, STAT_200, strlen(STAT_200), MSG_CONFIRM);    // # stat
        ext f_ext = get_ext(f_name);

        if (f_ext == cgi) {
            int red;
            if ((red = dup2(accept_fd, STDOUT_FILENO)) < 0) {
                perror("dup error");
                exit(1);
            }

            if (execlp(f_name, f_name, NULL) < 0) {
                perror("exec family error");
                exit(1);
            }

            close(accept_fd);
            exit(0);
        } else if (f_ext == gif) {
            strcat(response, F_GIF);
            // send(accept_fd, F_GIF, strlen(F_GIF), MSG_CONFIRM);

            FILE *f_open;
            char *f_cont = (char *) malloc(sizeof(char) * 524288);
            size_t f_size = f_stat.st_size;
            char *con_len = (char *) malloc(sizeof(char) * 64);

            sprintf(con_len, "Content-Length: %d\r\n\r\n", (int) f_size);
            strcat(response, con_len);
            write(accept_fd, response, strlen(response));
            // send(accept_fd, con_len, strlen(con_len), MSG_CONFIRM);  // Content-length: #\r\n

            if ((f_open = fopen(f_name, "r")) == NULL) {
                perror("fopen error");
                exit(1);
            }

            // int i;
            // char ch;
            // for (i = 0; i < f_size; i++) {
            //     ch = fgetc(f_open);
            //     send(accept_fd, &ch, 1, MSG_CONFIRM);
            // }

            fread(f_cont, sizeof(char), f_size, f_open);
            send(accept_fd, f_cont, f_size, MSG_CONFIRM);
            close(accept_fd);
            exit(0);
        } 

Here is the relevant part of the code, and I checked repeatedly whether I missed any \r or \n in the header by checking the string lengths, but no good. People who have experiences with this, can you note where the mistake is? Am I misusing any functions anywhere? I got rid of the part where it sends html files because it failed before so I was mainly working on transferring a gif file over.

Was it helpful?

Solution

It turns out the headers are supposed to start from the third line of what I thought was the correct header. So it should look like this:

    HTTP/1.1 200 OK\r\n
    Content-Type: text/html\r\n
    \r\n
    [file content]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top