在 C 中解析 HTTP 请求行

Parse HTTP Request Line In C

这是永远不会结束的问题。任务是在 C 中解析 Web 服务器中的请求行——长度不确定。我从 Web 上提取了以下内容作为示例。

GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1

我必须提取绝对路径:/path/script.cgi 和查询:?field1=value1&field2=value2。我被告知以下函数是关键:strchrstrcpystrncmpstrncpy、and/or strstr.

这是到目前为止发生的事情:我了解到使用 strchrstrstr 之类的函数绝对允许我在某些点 t运行 对请求行进行分类,但永远不会让我摆脱我不想要的请求行的部分,并且我如何对它们进行分层并不重要。

例如,这里有一些代码让我接近隔离查询,但我无法消除 http 版本。

bool parse(const char* line)
{
    // request line w/o method
    const char ch = '/';
    char* lineptr = strchr(line, ch);

    // request line w/ query and HTTP version
    char ch_1 = '?';
    char* lineptr_1 = strchr(lineptr, ch_1);

    // request line w/o query
    char ch_2 = ' ';
    char* lineptr_2 = strchr(lineptr_1, ch_2);

    printf("%s\n", lineptr_2);

    if (lineptr_2 != NULL)
        return true;
    else
        return false;
}

不用说,我在尝试隔离绝对路径时遇到了类似的问题(我可以放弃该方法,但不能放弃 ? 或之后的任何内容),而且我看不到任何可以使用需要的功能的场合我想知道 a priori 我想从一个位置(通常是数组)复制多少个字符到另一个位置,因为当这是 运行 实时时,我会事先不知道请求行会是什么样子。如果有人看到我遗漏的东西并能指出正确的方向,我将不胜感激!

前段时间我用 C 编写了一些函数,这些函数手动解析 C 字符串直到分隔符,类似于 C++ 中的 getline。

// Trims all leading whitespace along with consecutive whitespace from provided cstring into destination char*. WARNING: ensure size <= sizeof(destination)
void Trim(char* destination, char* source, int size)
{
    bool trim = true;
    int index = 0;
    int i;
    for (i = 0; i < size; ++i)
    {
        if (source[i] == '\n' || source[i] == '[=10=]')
        {
            destination[index++] = '[=10=]';
            break;
        }
        else if (source[i] != ' ' && source[i] != '\t')
        {
            destination[index++] = source[i];
            trim = false;
        }
        else if (trim)
            continue;
        else
        {
            if (index > 0 && destination[index - 1] != ' ')
                destination[index++] = ' ';
        }
    }
}

// Parses text up to the provided delimiter (or newline) into the destination char*. WARNING: ensure size <= sizeof(destination)
void ParseUpToSymbol(char* destination, char* source, int size, char delimiter)
{
    int index = 0;
    int i;
    for (i = 0; i < size; ++i)
    {
        if (source[i] != delimiter && source[i] != '\n' && source[i] != '[=10=]'  && source[i] != ' '))
        {
            destination[index++] = source[i];
        }
        else
        {
            destination[i] = '[=10=]';
            break;
        }
    }

    Trim(destination, destination, size);
}

然后您可以使用以下内容来解析您的 C 字符串:

char* buffer = (char*)malloc(64);
char* temp = (char*)malloc(256);
strcpy(temp, "GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1");
Trim(temp, temp, 256);
ParseUpToSymbol(buffer, cstr, 64, '?');
temp = temp + strlen(buffer) + 1;
Trim(temp, temp, 256);

上面的代码从目标字符串中删除任何前导和尾随的白色space,在本例中为"GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1",然后将解析后的值存储到变量缓冲区中。 运行 这第一次应该把单词 "GET" 放在缓冲区里面。当您执行 "temp = temp + strlen(buffer) + 1" 时,您正在重新调整临时字符指针,以便您可以使用字符串的其余部分再次调用 ParseUpToSymbol。如果你再次调用它,你应该得到通向第一个问号的绝对路径。您可以重复此操作以获取每个单独的查询字符串或将分隔符更改为 space 并获取 URL 的整个查询字符串部分。我想你应该已经明白了。当然,这只是众多解决方案中的一种。

更优雅的解决方案。

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

int parse(const char* line)
{
    /* Find out where everything is */
    const char *start_of_path = strchr(line, ' ') + 1;
    const char *start_of_query = strchr(start_of_path, '?');
    const char *end_of_query = strchr(start_of_query, ' ');

    /* Get the right amount of memory */
    char path[start_of_query - start_of_path];
    char query[end_of_query - start_of_query];

    /* Copy the strings into our memory */
    strncpy(path, start_of_path,  start_of_query - start_of_path);
    strncpy(query, start_of_query, end_of_query - start_of_query);

    /* Null terminators (because strncpy does not provide them) */
    path[sizeof(path)] = 0;
    query[sizeof(query)] = 0;

    /*Print */
    printf("%s\n", query, sizeof(query));
    printf("%s\n", path, sizeof(path));
}

int main(void)
{
    parse("GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1");
    return 0;
}