C或者C++写HTTP客户端还是libcurl比较强大,支持HTTP/HTTPS,而且Cookie的管理很强大。自己写HTTP消息免不了管理HTTP的头部,曾经就为了删除一个HTTP头部耗费了好几天,翻遍了谷歌百度还是只找到官方的方法,官方的文档中说删除HTTP消息头是这样的:
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Accept: Agent-007");
headers = curl_slist_append(headers, "Accept:"); //冒号后面不加内容就是删除头部
GET / HTTP/1.1
Host: www.baidu.com
Cookie:
Accept: Agent-007
如是是下面这么用:
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Accept: Agent-007");
headers = curl_slist_append(headers, "Accept:");
headers = curl_slist_append(headers, "Accept: XXX");
GET / HTTP/1.1
Host: www.baidu.com
Cookie:
Accept: Agent-007
Accept: XXX
头部删除不了,而且两个头部一样,肯定不合理啊。
遇到这个问题,把google翻到n多页之外都找不到解决办法,我想libcurl这么强大的库不会这么弱吧,没办法,问题还是要解决的,那就只能啃libcurl的源码了。
curl_slist就是一单链表:
struct curl_slist {
char *data;
struct curl_slist *next;
};
看看curl_slist_append的实现:
struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data)
{
struct curl_slist *last;
struct curl_slist *new_item;
DEBUGASSERT(data);
new_item = malloc(sizeof(struct curl_slist));
if(!new_item)
return NULL;
new_item->next = NULL;
new_item->data = data;
/* if this is the first item, then new_item *is* the list */
if(!list)
return new_item;
last = slist_get_last(list);
last->next = new_item;
return list;
}
struct curl_slist *curl_slist_append(struct curl_slist *list,
const char *data)
{
char *dupdata = strdup(data);
if(!dupdata)
return NULL;
list = Curl_slist_append_nodup(list, dupdata);
if(!list)
free(dupdata);
return list;
}
上面的代码也很容易,首先把参数data复制一份,然后调用Curl_slist_append_nodup函数来新建一个curl_slist链表节点,并保存复制之后的data指针,再看看list是否为NULL,为NULL则直接返回刚刚新建的链表节点,否则将新建的链表节点插入到list链表的尾部。整个curl_slist_append流程就是这样子,其实就是一个单列表的管理,那我们要删除HTTP消息头就比较简单了,我们来接管这个链表就可以了。写了个删除HTTP头部的函数:
bool curl_header_delete(struct curl_slist **csl_head, const char *data)
{
struct curl_slist *node;
struct curl_slist *head = *csl_head;
struct curl_slist *node_prev = NULL;
struct curl_slist *node_del = NULL;
const char *colon;
size_t head_name_len;
colon = strchr(data, ':');
if (colon == NULL) {
return false;
}
head_name_len = colon - data + 1;
node = head;
while (node != NULL) {
if (strnicmp(node->data, data, head_name_len) == 0) {
node_del = node;
node = node->next;
if (node_prev != NULL) {
node_prev->next = node_del->next;
} else {
*csl_head = node_del->next;
}
node_del->next = NULL;
curl_slist_free_all(node_del);
continue;
}
node_prev = node;
node = node->next;
}
return true;
}
要想删除某个头部只要像下面这样调用就行:
curl_header_delete(&headers, "Accept:");//为什么还要加冒号呢,是因为防止头部前缀一样的情况,其实也可以实现成不加冒号,只是处理起来麻烦一点而已
接下来看看为什么这么调用删除不了:
headers = curl_slist_append(headers, "Accept:");
看一下Curl_add_custom_headers的实现:
CURLcode Curl_add_custom_headers(struct connectdata *conn,
Curl_send_buffer *req_buffer)
{
char *ptr;
struct curl_slist *headers=conn->data->set.headers;
while(headers) {
ptr = strchr(headers->data, ':');
if(ptr) {
/* we require a colon for this to be a true header */
ptr++; /* pass the colon */
while(*ptr && ISSPACE(*ptr))
ptr++;
if(*ptr) {
/* only send this if the contents was non-blank */
if(conn->allocptr.host &&
/* a Host: header was sent already, don't pass on any custom Host:
header as that will produce *two* in the same request! */
checkprefix("Host:", headers->data))
;
else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
checkprefix("Content-Type:", headers->data))
;
else if(conn->bits.authneg &&
/* while doing auth neg, don't allow the custom length since
we will force length zero then */
checkprefix("Content-Length", headers->data))
;
else if(conn->allocptr.te &&
/* when asking for Transfer-Encoding, don't pass on a custom
Connection: */
checkprefix("Connection", headers->data))
;
else {
CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
headers->data);
if(result)
return result;
}
}
}
else {
ptr = strchr(headers->data, ';');
if(ptr) {
ptr++; /* pass the semicolon */
while(*ptr && ISSPACE(*ptr))
ptr++;
if(*ptr) {
/* this may be used for something else in the future */
}
}
else {
if(*(--ptr) == ';') {
CURLcode result;
/* send no-value custom header if terminated by semicolon */
*ptr = ':';
result = Curl_add_bufferf(req_buffer, "%s\r\n",
headers->data);
if(result)
return result;
}
}
}
}
headers = headers->next;
}
return CURLE_OK;
}
(全文完)