在 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
。我被告知以下函数是关键:strchr
、strcpy
、strncmp
、strncpy
、and/or strstr
.
这是到目前为止发生的事情:我了解到使用 strchr
和 strstr
之类的函数绝对允许我在某些点 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;
}
这是永远不会结束的问题。任务是在 C 中解析 Web 服务器中的请求行——长度不确定。我从 Web 上提取了以下内容作为示例。
GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1
我必须提取绝对路径:/path/script.cgi
和查询:?field1=value1&field2=value2
。我被告知以下函数是关键:strchr
、strcpy
、strncmp
、strncpy
、and/or strstr
.
这是到目前为止发生的事情:我了解到使用 strchr
和 strstr
之类的函数绝对允许我在某些点 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;
}