在 C 服务器上保存通过 POST 接收的文件的问题

问题描述 投票:0回答:0

对于我的班级,我们正在用 C 编写一个服务器。

此服务器必须有一些可用的网站,并接收和存储它通过 POST 接收的任何文件。我们已经有了网站的 GET 部分,但是当涉及到存储任何传入文件时,我们遇到了更多麻烦,我创建了一个解析器来解析请求。但是当请求的主体不是纯文本(而是图像或 pdf 文档)然后将其存储在请求结构的主体中(然后存储在具有相应的新文件中)时,我无法保存请求的主体扩展。

这是我的解析器的代码:

// Analiza la línea de solicitud HTTP para determinar el método, la ruta y el host
void parse_request_line(char *buffer, http_request *request)
{
    char *method_end = strchr(buffer, ' ');
    if (method_end == NULL)
    {
        request->method = UNSUPPORTED;
        return;
    }
    size_t method_len = method_end - buffer;
    if (strncmp(buffer, "GET", method_len) == 0)
    {
        request->method = GET;
    }
    else if (strncmp(buffer, "POST", method_len) == 0)
    {
        request->method = POST;
    }
    else if (strncmp(buffer, "PUT", method_len) == 0)
    {
        request->method = PUT;
    }
    else if (strncmp(buffer, "DELETE", method_len) == 0)
    {
        request->method = DELETE;
    }
    else
    {
        request->method = UNSUPPORTED;
    }
    printf("--> Método HTTP: %d\n", request->method);

    char *uri_start = method_end + 1;
    char *uri_end = strchr(uri_start, ' ');
    if (uri_end == NULL)
    {
        request->method = UNSUPPORTED;
        return;
    }
    size_t uri_len = uri_end - uri_start;
    char *http_version_start = uri_end + 1;
    if (http_version_start[0] != 'H' || http_version_start[1] != 'T' || http_version_start[2] != 'T' || http_version_start[3] != 'P' || http_version_start[4] != '/')
    {
        request->method = UNSUPPORTED;
        printf("-->http_version_start no comenza con HTTP/. ");
        return;
    }
    if (http_version_start[5] == '0')
    {
        strcpy(request->version, "HTTP/1.0");
        printf("-->HTTP version 1.0 detectado. \n");
    }
    else if (http_version_start[5] == '1')
    {
        strcpy(request->version, "HTTP/1.1 ");
        printf("-->HTTP version 1.1 detectado. \n");
    }
    else
    {
        request->method = UNSUPPORTED;
        printf("--> No se puede detectar la version de HTTP o no esta soportado. \n");
        return;
    }
    request->version[7] = '\0';
    char *uri = strndup(uri_start, uri_len);
    char *host_start = strstr(uri, "://");
    if (host_start)
    {
        host_start += 3;
        char *path_start = strchr(host_start, '/');
        if (path_start)
        {
            *path_start = '\0';
            path_start++;
            request->host = strdup(host_start);
            request->path = strdup(path_start);
        }
        else
        {
            request->host = strdup(host_start);
            request->path = strdup("");
        }
    }
    else
    {
        // Trata de obtener host desde antes

        char *host_start = strstr(method_end, "Host: ");
        if (host_start)
        {
            host_start += 6;
            char *host_end = strstr(host_start, "\r\n");
            size_t host_len = host_end - host_start;
            request->host = strndup(host_start, host_len);
        }
        else
        {
            request->host = strdup("");
        }
        request->path = strdup(uri);
    }
    printf("->> Host: %s\n", request->host);
    printf("->> Ruta: %s\n", request->path);
    free(uri);

    if (request->method == POST)
    {
        char *content_type_start = strstr(method_end, "Content-Type: ");
        if (content_type_start != NULL)
        {
            content_type_start += 14;
            char *content_type_end = strstr(content_type_start, "\r\n");
            size_t content_type_len = content_type_end - content_type_start;
            request->content_type = strndup(content_type_start, content_type_len);

            // Detectar el tipo de archivo y asignar la extensión correspondiente
            if (strncmp(request->content_type, "text/plain", 10) == 0) {
                request->file_ext = strdup(".txt");
            } else if (strncmp(request->content_type, "image/png", 9) == 0) {
                request->file_ext = strdup(".png");
            } else if (strncmp(request->content_type, "image/jpeg", 9) == 0) {
                request->file_ext = strdup(".jpeg");
            } else if (strncmp(request->content_type, "image/gif", 15) == 0) {
                request->file_ext = strdup(".gif");
            } else if (strncmp(request->content_type, "application/pdf", 15) == 0) {
                request->file_ext = strdup(".pdf");
            } else if (strncmp(request->content_type, "application/octet-stream", 24) == 0) {
                request->file_ext = strdup(".exe");
            } else {
                request->file_ext = strdup(".dat");
            }
        }

        char *content_length_start = strstr(method_end, "Content-Length: ");
        if (content_length_start != NULL)
        {
            content_length_start += 16; // saltar los caracteres de "Content-Length:"
            char *content_length_end = strstr(content_length_start, "\r\n");
            size_t content_length_len = content_length_end - content_length_start;
            char content_length_str[content_length_len + 1];
            strncpy(content_length_str, content_length_start, content_length_len);
            content_length_str[content_length_len] = '\0';
            request->content_len = strtol(content_length_str, NULL, 10);
        }
        

        char *body_start = strstr(buffer, "\r\n\r\n");
        if (body_start != NULL) 
        {
            body_start += 4; // saltar los caracteres de separación
            size_t buffer_len = strlen(buffer);
            size_t body_len = buffer + buffer_len - body_start - 4;
            request->body = (char*) malloc(body_len + 1); // incluir espacio adicional para NULL
            memcpy(request->body, body_start, body_len);
            request->body[body_len] = '\0'; // agregar byte NULL

            // Generar el nombre del archivo a partir de la fecha y hora
            time_t t = time(NULL);
            struct tm tm = *localtime(&t);
            char filename[100];
            sprintf(filename, "%04d-%02d-%02d_%02d-%02d-%02d%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, request->file_ext);


           // Guardar el archivo en disco
            int fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
            write(fd, request->body, request->content_len);
            //write(fd, request->body, body_len);
            close(fd);
        }
        printf("->> Body: %s\n", request->body);
    }
    printf("->> THE BUFFER: %s\n", buffer);
}

最重要的是身体状况之后的部分:

if (request->method == POST)
    {
        char *content_type_start = strstr(method_end, "Content-Type: ");
        if (content_type_start != NULL)
        {
            content_type_start += 14;
            char *content_type_end = strstr(content_type_start, "\r\n");
            size_t content_type_len = content_type_end - content_type_start;
            request->content_type = strndup(content_type_start, content_type_len);

            // Detectar el tipo de archivo y asignar la extensión correspondiente
            if (strncmp(request->content_type, "text/plain", 10) == 0) {
                request->file_ext = strdup(".txt");
            } else if (strncmp(request->content_type, "image/png", 9) == 0) {
                request->file_ext = strdup(".png");
            } else if (strncmp(request->content_type, "image/jpeg", 9) == 0) {
                request->file_ext = strdup(".jpeg");
            } else if (strncmp(request->content_type, "image/gif", 15) == 0) {
                request->file_ext = strdup(".gif");
            } else if (strncmp(request->content_type, "application/pdf", 15) == 0) {
                request->file_ext = strdup(".pdf");
            } else if (strncmp(request->content_type, "application/octet-stream", 24) == 0) {
                request->file_ext = strdup(".exe");
            } else {
                request->file_ext = strdup(".dat");
            }
        }

        char *content_length_start = strstr(method_end, "Content-Length: ");
        if (content_length_start != NULL)
        {
            content_length_start += 16; // saltar los caracteres de "Content-Length:"
            char *content_length_end = strstr(content_length_start, "\r\n");
            size_t content_length_len = content_length_end - content_length_start;
            char content_length_str[content_length_len + 1];
            strncpy(content_length_str, content_length_start, content_length_len);
            content_length_str[content_length_len] = '\0';
            request->content_len = strtol(content_length_str, NULL, 10);
        }
        

        char *body_start = strstr(buffer, "\r\n\r\n");
        if (body_start != NULL) 
        {
            body_start += 4; // saltar los caracteres de separación
            size_t buffer_len = strlen(buffer);
            size_t body_len = buffer + buffer_len - body_start - 4;
            request->body = (char*) malloc(body_len + 1); // incluir espacio adicional para NULL
            memcpy(request->body, body_start, body_len);
            request->body[body_len] = '\0'; // agregar byte NULL

            // Generar el nombre del archivo a partir de la fecha y hora
            time_t t = time(NULL);
            struct tm tm = *localtime(&t);
            char filename[100];
            sprintf(filename, "%04d-%02d-%02d_%02d-%02d-%02d%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, request->file_ext);


           // Guardar el archivo en disco
            int fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
            write(fd, request->body, request->content_len);
            //write(fd, request->body, body_len);
            close(fd);
        }
        printf("->> Body: %s\n", request->body);
    }

我已经测试过,并且 http_request 结构被保存了,除了请求的主体。
当我尝试向服务器发送一个简单的 png 图片时,在控制台上可以看到:

pc@pc server % ./server              
Usando assets de  ./logs
Servidor iniciado en el puerto 8080...
--> Método HTTP: 1
-->HTTP version 1.1 detectado. 
->> Host: 127.0.0.1:8080
->> Ruta: /test2
->> Body: �PNG
->> THE BUFFER: POST /test2 HTTP/1.1
User-Agent: PostmanRuntime/7.32.2
Accept: */*
Postman-Token: 13ebab0b-b17c-4a89-916c-d0889bacd9f9
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 86054
Content-Type: image/png

�PNG


--Method: 1
--Host: 127.0.0.1:8080
--Path: /test2
--Body: �PNG
--Content-Length: 86054
--Content-Type: image/png

正如我们在以“--”开头的最后几行中看到的那样,除了请求的正文之外,数据被正确解析,我如何正确保存或解析请求的正文以便能够保存传入的文件正确吗?

我们可以对比POSTMAN请求和存储的数据,Content-Length和Type似乎是正确的,路径和方法也是可以的。这只是我难以管理的身体。我们还尝试使用 CURL 来保存二进制文件,但对于我们需要的东西来说它似乎太乱了。

我们只需要将人们发送给我们的任何文本/图像/pdf 存储在 POST 请求的正文中。

对不起任何拼写错误,我有点睡眠不足,但非常感谢任何帮助!!

总结:我们尝试将数据存储在请求的主体中,并使用 write 将其保存到新文件中,但是当请求是二进制文件而不是纯文本时,它没有正确保存。

c post server
© www.soinside.com 2019 - 2024. All rights reserved.