user2633604
user2633604

Reputation: 111

Send image through C socket

void callback (struct Request req) {

    char buffer[8196];
    int file;
    bzero(buffer, sizeof(buffer));

    if(!strncmp(req.method, "GET", 3)){

        if (!strcmp(req.path, "/")){

            memcpy(buffer, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n", 44);
            response(buffer);

            int file = open("./static/index.html", O_RDONLY);
            long ret;

            while ((ret = read(file, buffer, 8196)) > 0) {
                response(buffer);   
            }

        }else if (!strcmp(req.path, "/login")){

            memcpy(buffer, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n", 44);
            response(buffer);

            int file = open("./static/login.html", O_RDONLY);
            long ret;

            while ((ret = read(file, buffer, 8196)) > 0) {
                response(buffer);   
            }

        }else{

            char path[80];
            char header[240];

            sprintf(path, "./static%s", req.path);
            int ext = readFile(path, buffer);
            char type[15];
            struct stat st;

            if(!ext){
                strcpy(type, "text/html\0");
            }else if(ext == 1){
                strcpy(type, "text/javascript\0");
            }else if(ext == 2){
                strcpy(type, "text/css\0");
            }else if(ext == 3){
                strcpy(type, "image/jpeg\0");
            }

            stat(path, &st);
            sprintf(header, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %lld\r\n\r\n", type, st.st_size);
            response(header);

            int file = open(path, O_RDONLY);
            long ret;

            while ((ret = read(file, buffer, 8196)) > 0) {
                buffer[8196-1] = 0;
                response(buffer);   
            }

        }

    }else if(!strncmp(req.method, "POST", 4)){

    }

    bzero(buffer, sizeof(buffer));

};

void response (char message[]) {
    write(clientsocket, message, strlen(message));
};

This is the code I have for my HTTP server coded in C. The callback function is executed everytime the socket receives a request. The struct Request has 2 char [], method that can be POST or GET, and path, which contains the request's path. The server sends properly the static files such html, js and css files. It serves the bootstrap statics properly, although if buffer[8196-1] = 0; is not when I send the file, the file contains wrong characters.

I try to send a .jpg image, and the headers are properly sended, but the file is not. When I access to 127.0.0.1:8080/image.jpg, Firefox displays an error that the image contains errors and can't be displayed, or just display a bunch of caracters and the server stops serving the rest of js and css files.

I read all the questions releated to this and I didn't file a solution jet. The image size is 1'1Mb, maybe is that the problem? I think not because I send the data in 'pieces', but I'm not sure at all. The program doesn't show me an error about the file, seems to be opened and read correctly.

It's a header problem maybe? The rest of the js and css files are sended in the same pice of code that the image (the else statement), ad they don't have any trouble, and the FF console show me the headers of every file, and they're correct so I think that's not the problem.

Thank's advanced!

Upvotes: 1

Views: 4909

Answers (3)

Remy Lebeau
Remy Lebeau

Reputation: 598021

When the client requests either / or /login, you are not sending any Content-Length or Transfer-Encoding: chunked response header. Without that, the client can't know whether a message body exists or when to stop reading it, unless you close the socket connection immediately after sending the response (see RFC 1945 Section 7.2.2 and RFC 2616 Section 4.4). Which you should be doing anyway since you are sending an HTTP 1.0 response without a Connection: keep-alive response header.

You should not be doing anything with buffer[8196-1] at all. You should be telling response() how many bytes are actually in buffer and let it send the buffer as-is up to that number of bytes. Don't rely on strlen() for binary data, since binary data can contain embedded nulls.

You should be sending an HTTP error if open() fails to open the file being requested.

With that said, try something more like this instead:

void callback (struct Request req)
{
    char buffer[8196];
    int file;
    bzero(buffer, sizeof(buffer));

    if (strncmp(req.method, "GET") == 0)
    {
        char path[258];
        char header[240];
        char *type;

        if (strcmp(req.path, "/") == 0)
        {
            strcpy(path, "./static/index.html");
            type = "text/html";
        }
        else if (strcmp(req.path, "/login") == 0)
        {
            strcpy(path, "./static/login.html");
            type = "text/html";
        }
        else
        {
            sprintf(path, "./static%.258s", req.path);
            // or: snprintf(path, 258, "./static%s", req.path); 

            int ext = determineFileType(path);
            switch (ext)
            {
                case 0: // HTML
                    type = "text/html";
                    break;
                case 1: // Javascript
                    type = "text/javascript";
                    break;
                case 2: // CSS
                    type = "text/css";
                    break;
                case 3: // JPG
                    type = "image/jpeg";
                    break;
                default: // something else
                    type = "application/octet-stream";
                    break;
            }
        }

        int file = open(path, O_RDONLY);
        if (file == -1)
        {
            // send different errors depending on the failure, like 404 if the file is not found, etc...
            if (errno == ENOENT) {
                strcpy(header, "HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n");
            } else {
                strcpy(header, "HTTP/1.0 500 Internal Error\r\nContent-Length: 0\r\n\r\n");
            }
            response(header, strlen(header));
        }
        else
        {
            struct stat st;
            stat(path, &st);

            sprintf(header, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %lld\r\n\r\n", type, st.st_size);
            response(header, strlen(header));   

            long ret;
            while ((ret = read(file, buffer, sizeof(buffer))) > 0)
                response(buffer, ret);   

            close(file);
        }
    }
    else if (strncmp(req.method, "POST") == 0)
    {
        //...
    }
}

void response (void *message, int msglen)
{
    char *msg = (char*) message;

    while (msglen > 0)
    {
         int len = write(clientsocket, msg, msglen);
         if (len <= 0) return;
         msg += len;
         msglen -= len;
    }
}

Upvotes: 3

HTTP is a complex protocol (the specifications have hundreds of pages). So better use some existing libraries for it. On the server side (to embed some web server in your application), use e.g. libonion. On the client side (to make HTTP requests in your application), use e.g. libcurl

BTW, you probably could strace(1) your program to understand the really executed syscalls(2).

Upvotes: 2

sth
sth

Reputation: 229854

The call to write() in response() does not necessarily write the complete buffer to the socket.

If there is already to much data buffered by the OS it might only write a part of the data in the buffer and you will need another (or several more) calls to write() to get everything transmitted. To see how much data was transmitted look at the return value ofwrite(). It returns the number that were written.

Additionally you use strlen() to determine the length of the data to transmit, but images are not strings. They can contain zero bytes and strlen() will not give useful results. Better pass the length of the data explicitly to the response() function.

Also, when reading the image file, buffer[8196-1] = 0 will overwrite bytes of the image with zeros.

Upvotes: 2

Related Questions