libcurl删除HTTP头部方法

        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:"); //冒号后面不加内容就是删除头部


但是实践证明这样是删除不了的,发出去的HTTP头如下:

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"); 


按理说应该只保留这个头部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;
}


先是找冒号,找到冒号之后忽略冒号之后的空白,再看看空白之后有没有内容,没有则不添加到头部的buffer中,所以……


(全文完)




  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值