Search code examples
csocketsnetwork-programming

C socket programming sending an image file "the image cannot be displayed because it contains an error"


I've been working on a project where I'm implementing an HTTP server in C, and I'm encountering an issue with rendering images in a web browser. Every time I try to access the image via the server, I receive the error message "the image cannot be displayed because it contains an error."

I've checked my code thoroughly, but I'm unable to pinpoint the exact problem. Could someone please guide me on what I might be doing wrong? Below is a simplified version of my code for serving images:

main.c

#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT "8080"
#define BUFFSIZE 2048
#define RESSIZE 4096
#include "filehandle.h"
#include "httpresponse.h"

char *get_file_path(const char *request) {
    char *path = NULL;
    char *start = strstr(request, "GET ");
    if (start != NULL) {    start += 4; // Move past "GET "     char *end = strstr(start, " ");     if (end != NULL) {
        int path_length = end - start;
        path = (char *)malloc(path_length + 1);
        strncpy(path, start, path_length);
        path[path_length] = '\0';   }
    }
    return path; };

char *requestpath(int sockfd) {
    char *buffer = (char *)malloc(BUFFSIZE);

    int bytes = read(sockfd, buffer, BUFFSIZE);
    return buffer; };

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

    int status;
    struct addrinfo hints, *res;
    int sockfd;
    int bindstatus;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    status = getaddrinfo(NULL, PORT, &hints, &res);
    if (status == -1) {     fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
    }

    sockfd = socket(res->ai_family, res->ai_socktype, 0);
    if (sockfd == -1) {     fprintf(stderr, "socket error %s\n", strerror(sockfd));
    }

    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));

    bindstatus = bind(sockfd, res->ai_addr, res->ai_addrlen);
    if (bindstatus == -1) {     fprintf(stderr, "bind error %s\n", strerror(bindstatus));
    }

    freeaddrinfo(res);

    int l;
    l = listen(sockfd, 10);
    if (l != 0) {   fprintf(stderr, "LISTEN ERROR %s\n", strerror(l));
    }

    while (1) {     struct sockaddr_in client_addr;     int newfd;  socklen_t theiraddr = sizeof client_addr;   newfd = accept(sockfd, (struct sockaddr *)&client_addr, &theiraddr);

    if (newfd == -1) {
        fprintf(stderr, "accept error %s\n", strerror(errno));  } else {
        char str[INET_ADDRSTRLEN];
        inet_ntop(client_addr.sin_family, (struct inaddr *)&client_addr.sin_addr,
            str, INET_ADDRSTRLEN);
        printf("connection from %s\n", str);    }

    char *request = requestpath(newfd);     char *filepath = get_file_path(request);    printf("%s\n", filepath);   free(request);

    char *response = NULL;

    if (strcmp(filepath, "/") == 0 || strcmp(filepath, "/index.html") == 0) {
        response = content("index.html");

        char *http_response = (char *) malloc(RESSIZE);
        char *date = get_date_for_server();
        int content_length = strlen(response);
        int len,bytes_sent;
        int n = snprintf(http_response,RESSIZE,"HTTP/1.1 200 OK\r\nAcess-Control-Allow-Origin: *\r\nConnection: Keep-Alive\r\nDate: %s\r\nContent-Type: %s\r\nContent-Length: %llu\r\n\n%s",date,"text/html",content_length,response);
        len = strlen(http_response);
        bytes_sent = send(newfd,http_response,len,0);
        free(date);
        free(http_response);

        shutdown(newfd, SHUT_WR);
    }
    else if(strcmp(filepath,"/image.jpg") == 0){
        char http_header[256];
        char *date = get_date_for_server();

        struct file_data *fdata = NULL;
        fdata = read_file("image.jpg");
        if(fdata == NULL)
            fprintf(stderr,"Error opening binary file");
   
        uint8_t buff[fdata->length];
        FILE *wbf = fopen("test.jpg","wb");
        fwrite(buff,fdata->length,1,wbf);
        fclose(wbf);
        snprintf(http_header,256,"HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nContent-Length: %llu\r\n",fdata->length);
        send(newfd,http_header,strlen(http_header),0);
        send(newfd,fdata->data,fdata->length,0);
        printf("length is %ld\n",fdata->length);
        shutdown(newfd, SHUT_WR);
        fdata = NULL;

    }
    else
        shutdown(newfd, SHUT_WR);
    }
    close(sockfd);
    return 0; }

here is my filehandle.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define BUFFSIZE 2048

typedef struct file_data {
    uint8_t *data;
    size_t length;
}file_data;


struct file_data *read_file(const char *path){

        FILE *handler = NULL;
        struct file_data *fdata = NULL;
        size_t read_length = 0;
        uint8_t buffer[BUFFSIZE] = {0x00};

        fdata = malloc(sizeof(struct file_data));
        if(fdata == NULL)
            return NULL;

        bzero(fdata, sizeof(struct file_data));

        fdata->data = malloc(1);
        if(fdata->data == NULL){
            free(fdata);
            return NULL;
        }

        handler = fopen(path,"rb");
        if(handler == NULL){
            free(fdata->data);
            free(fdata);
            return NULL;
        }

        while(!feof(handler)){
            read_length = fread(buffer,1,BUFFSIZE,handler);
            fdata->data = realloc(fdata->data,fdata->length + read_length);
            if(fdata->data == NULL){
                fclose(handler);
                free(fdata);
                return NULL;
            }
            memcpy(fdata->data +fdata->length, buffer, read_length);
            fdata->length += read_length;
        }

        fclose(handler);

        return fdata;
}


// open the requested file and return the file pointer
FILE* requestfile(char *filename){
    FILE *fp;
    fp = fopen(filename,"r");
    if(fp != NULL){
        return fp;
    }
    else{
        perror("Error opening file\n");
    }
}

// count the byte size of file
int count_file_bytes(FILE *fp)
{
    int c,count = 0;
    if(fp != NULL){
        while((c = fgetc(fp)) != EOF)
            count++;
    }
    fclose(fp);
    return count;
}

// read the file content to buffer and return
char* content(char *filename)
{
    FILE *fp = requestfile(filename);
    int bytecount = count_file_bytes(fp);
    char *content = (char *) malloc(bytecount+1);

    FILE *file = requestfile(filename);
    size_t i;

    for(i=0;i<(bytecount+1);++i)
    {
        int c = fgetc(file);
        if( c == EOF)
        {
            break;
        }

        content[i]= c;
    }

    content[i] = '\0';
    fclose(file);
    return content;



}

// get file extension before last '.'
char *get_file_ext(char *filename, int c)
{
    char *type = strrchr(filename,c);
    return type;
}


// read the file in binary format
char *read_binary_file(char *filename)
{
    FILE *fp = fopen(filename,"rb");
    if(fp == NULL)
        fprintf(stderr,"\t Error Opening file: %s\n",filename);
    long image_size;
    int c;
    int count = 0;
    fseek(fp,0,SEEK_END);
    image_size = ftell(fp);
    rewind(fp);
    char *buffer = (char *)malloc(image_size);

    fread(buffer,1,image_size,fp);

    fclose(fp);
    return buffer;
}

Solution

  • The issue was missing the \r\n in the header and content. this solved the issue