在解析HTTP响应时,我需要将响应头值分配给结构中匹配的头字段。比如说
struct http_response {
char* content_type;
char* date;
char* server;
char* via;
char* connection;
char* expires;
};
HTTP响应。
HTTP/1.1 200 OK
Content-Type: text/html; charset=ISO-8859-1
Date: Wed, 10 Jun 2020 17:30:46 GMT
Server: gws
Expires: Wed, 10 Jun 2020 17:30:46 GMT
Cache-Control: private
Via: 1.1 localhost (squid/4.11)
Connection: close
映射
http_response->content_type = "text/html; charset=ISO-8859-1"
http_response->date = "Wed, 10 Jun 2020 17:30:46 GMT"
/* ... */
天真的做法是 strcasecmp()
'ing并将值分配给结构字段;这对于小尺寸的结构似乎是可以的。但我有35 - 40个字段,而写40个 if-else
块似乎是最后的手段。利用结构的内存布局,以及同质场(char*
),我可以调整一下,以消除 if-else
块?
我认为有两种可能的选择。
创建一个表,将头字段名称映射到 补偿 变成 struct http_response
,比如像这样。
#include <stddef.h>
struct table_entry {
const char *name;
size_t offset;
};
static const table_entry table[] = {
{"Content-Type", offsetof(struct table_entry, content_type)},
{"Date", offsetof(struct table_entry, date)},
...
};
然后在解析每个头行的时候, 搜索那个表找到匹配的字段名, 然后你就知道在结构中的哪个位置放置值. 如果你确保表的排序方式是 key
那么你可以使用 bsearch()
来找到一个对数时间的键。
static int compare_entries(const void *a, const void *b) {
const struct table_entry *entry_a = a;
const struct table_entry *entry_b = b;
return strcasecmp(entry_a->name, entry_b->name);
}
...
struct http_response response = {NULL};
// Parse a line from the header
char *field = /* header field name */;
char *value = /* value */;
// Search for the key in the table
struct table_entry search_entry = {field};
struct table_entry *match = bsearch(&search_entry, table, sizeof(table) / sizeof(*table), sizeof(*table), compare_entries);
// If it's found, update response
if (match) {
char **value_ptr = (char **)((char *)&response + match->offset);
*value_ptr = value;
}
有很多可能的HTTP头字段,但一个典型的头块将只包含其中的几个。不建立 struct header_field
但只需制作一个函数来查询当前头块中所需的字段。如果你想对一个单一的头块进行大量的查询,那么假设你已经把它分割成了单独的行,你可能会想在行中使用 qsort()
首先,您可以使用 bsearch()
再次。
你可以使用你感兴趣的头信息表和结构中相应指针的字节偏移量。然后测试HTTP请求中是否有匹配的头文件。如果找到了就把它分配到结构体中相应指针所在的相应偏移处。
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
typedef struct http_response {
char* content_type;
char* date;
char* server;
char* via;
char* connection;
char* expires;
} http_response;
// a type to hold header name and a byte offset in the structure
typedef struct keyval
{
const char *key;
size_t offset;
} keyval;
// The headers we are interest in and their byte offsets
// from the beginning of the structure
static const keyval kv[] = {
{"Content-Type", offsetof(http_response, content_type)},
{"Date", offsetof(http_response, date)},
{"Server", offsetof(http_response, server)},
{"Via", offsetof(http_response, via)},
{"Connection", offsetof(http_response, connection)},
{"Expires", offsetof(http_response, expires)},
{NULL, 0}
};
// check each headere line and assign it to the corresponding
// structure field, if it is a header we are interested in
bool assign_header(http_response *resp, const char *hdr_line)
{
bool found = false;
char *p = strchr(hdr_line, ':');
char *pr = (char *)resp;
if (p)
{
int i;
*p = 0;
for (i=0; kv[i].key != NULL; i++)
if (strcmp(kv[i].key, hdr_line) == 0)
{
*(char **)(pr + kv[i].offset) = p + 1;
found = true;
break;
}
}
return found;
}
// test code
int main()
{
int i;
http_response resp;
char test[] =
"Content-Type: text/html; charset=ISO-8859-1\n\
Date: Wed, 10 Jun 2020 17:30:46 GMT\n\
Server: gws\n\
Expires: Wed, 10 Jun 2020 17:30:46 GMT\n\
Cache-Control: private\n\
Via: 1.1 localhost (squid/4.11)\n\
Connection: close\n\
";
memset(&resp, 0, sizeof(resp));
char *rest = test;
char *p =strtok_r(test, "\n", &rest);
while (p)
{
assign_header(&resp, p);
p =strtok_r(NULL, "\n", &rest);
}
// test a few fields
printf("Content-Type => %s\n", resp.content_type);
printf("Server => %s\n", resp.server);
printf("Expires => %s\n", resp.expires);
return 0;
}
发完帖子后,我看到也有人发了一个类似逻辑的答案:)
上面的代码将指针分配给结构体。你不妨修改一下,改成分配值的分配副本,以后再写一个函数来释放所有分配的内存。