【问题标题】:libcurl C++: retrieving HTTP response code when multiplexinglibcurl C++:多路复用时检索 HTTP 响应代码
【发布时间】:2021-08-04 08:28:26
【问题描述】:

目标:

仅在检测到某个HTTP status code(例如200)时才打印多路复用响应的内容。这需要在回调函数收到响应时读取标头并提取 HTTP 代码。

由于多路复用接收到的响应可以通过回调方法以任意顺序返回给程序,因此必须异步/以非阻塞方式查找此状态码。

下面看到的程序基于libcurl tutorial found here。包含有关HTTP multiplexing can be found here. 信息的问题

问题:

程序成功地异步发送请求,接收,然后打印响应,但我目前不确定在程序中的哪个位置使用curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code),或者这是否是multi interface 程序在查找时的正确方法HTTP status code

当前输出:

程序打印到stdout 完整的标头和有效负载,但是当使用上述CURLINFO_RESPONSE_CODE 时,程序的行为是同步的(即每次收到从标头到有效负载的任何信息时,程序都会打印响应代码(而它只有当回调函数dump(...)检测到它时才应该打印它。

代码:

#include <stdlib.h>
#include <errno.h>  
#include <iostream>
#include <string>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>
 
/* curl stuff */
#include <curl/curl.h>
#include <curl/mprintf.h>
 
#ifndef CURLPIPE_MULTIPLEX
#define CURLPIPE_MULTIPLEX 0
#endif

struct transfer {
  CURL *easy;
  unsigned int num;
  FILE *out;
};

#define NUM_HANDLES 1000

static void dump(const char *text, int num, unsigned char *ptr, size_t size,
          char nohex)
{
    // Print the response
    for (int i = 0; i < size; i++) {
        std::cout << ptr[i];
    }
}
 
static int my_trace(CURL *handle, curl_infotype type,
             char *data, size_t size,
             void *userp)
{
  const char *text;
  struct transfer *t = (struct transfer *)userp;
  unsigned int num = t->num;
  (void)handle; /* prevent compiler warning */
 
  switch(type) {
  default: /* in case a new one is introduced to shock us */
    return 0;
 
  case CURLINFO_SSL_DATA_OUT:
    text = "=> Send SSL data";
    break;
  case CURLINFO_HEADER_IN:
    text = "<= Recv header";
    break;
  case CURLINFO_DATA_IN:
    text = "<= Recv data";
    break;
  case CURLINFO_SSL_DATA_IN:
    text = "<= Recv SSL data";
    break;
  }

  dump(text, num, (unsigned char *)data, size, 1);
  return 0;
}
 
static void setup(struct transfer *t, int num)
{
  char filename[128];
  CURL *hnd;
 
  hnd = t->easy = curl_easy_init();
 
  curl_msnprintf(filename, 128, "dl-%d", num);
 
  t->out = fopen(filename, "wb");
  if(!t->out) {
    std::cout << "ERROR: could not open file for writing" << std::endl;
    exit(1);
  }
 
  /* write to this file */
  curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t->out);
 
  /* set the same URL */
  curl_easy_setopt(hnd, CURLOPT_URL, "https://sometesturl.xyz");
 
  /* please be verbose */
  curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
  curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);
  curl_easy_setopt(hnd, CURLOPT_DEBUGDATA, t);
 
  /* HTTP/2 please */
  curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);

  /* we use a self-signed test server, skip verification during debugging */
  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
 
#if (CURLPIPE_MULTIPLEX > 0)
  /* wait for pipe connection to confirm */
  curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
#endif

}
 
/*
 * Download many transfers over HTTP/2, using the same connection!
 */
int main(int argc, char **argv)
{

  struct transfer trans[NUM_HANDLES];
  CURLM *multi_handle;
  int i;
  int still_running = 0; /* keep number of running handles */
  int num_transfers;
  if(argc > 1) {
    /* if given a number, do that many transfers */
    num_transfers = atoi(argv[1]);
    if((num_transfers < 1) || (num_transfers > NUM_HANDLES))
      num_transfers = 3; /* a suitable low default */
  }
  else
    num_transfers = 3; /* suitable default */
 
  /* init a multi stack */
  multi_handle = curl_multi_init();
 
  for(i = 0; i < num_transfers; i++) {
    setup(&trans[i], i);
 
    /* add the individual transfer */
    curl_multi_add_handle(multi_handle, trans[i].easy);
  }
 
  curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
 
  do {
    CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
 
    if(still_running)
      /* wait for activity, timeout or "nothing" */
      mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
 
    if(mc)
      break;

  } while(still_running);

  // This behaves in a blocking / synchronous manner...
  // Not sure if this is the correct place to extract the status code?
  for (int i = 0; i < num_transfers; i++) {
      long response_code; // test variable
      curl_easy_getinfo(trans[i].easy, CURLINFO_RESPONSE_CODE, &response_code);
  }

  for(i = 0; i < num_transfers; i++) {
    curl_multi_remove_handle(multi_handle, trans[i].easy);
    curl_easy_cleanup(trans[i].easy);
  }
 
  curl_multi_cleanup(multi_handle);  
  return 0;
}

总结性问题:

1. 当使用multiplexing 可以以任何顺序接收响应时,如何使用 libcurl 提取 HTTP 状态代码,以便可以像当前正在接收响应一样异步找到它作为?

【问题讨论】:

  • 虽然呈现为 C++ 问题:您需要对其进行重组。用 C++ 包装漂亮的 libcurl 库不会使您面临的任何问题隐藏在大量代码块中。全部使用 C++,它会更容易诊断。
  • 感谢您的帮助 - 您的意思是我在这里错误地混合了 C 和 C++ 还是我错过了您的意思?
  • 不,你是对的。以 RAII 方式限制每一项资产,并让其通过上述方式销毁。

标签: c++ curl libcurl http2 multiplexing


【解决方案1】:

将调用移至主循环内的curl_easy_getinfo(CURLINFO_RESPONSE_CODE)。在循环中使用curl_multi_info_read() 来检测每个请求何时完成,然后再检索其响应代码。例如:

do {
    CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
 
    if (still_running) {
        /* wait for activity, timeout or "nothing" */
        mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
    }
 
    if (mc)
        break;

    do {
        int queued;
        CURLMsg *msg = curl_multi_info_read(multi_handle, &queued);
        if ((msg) && (msg->msg == CURLMSG_DONE) && (msg->result == CURLE_OK)) {
            long response_code;
            curl_easy_getinfo(mg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code);
            ...
        }
    }
    while (msg);
}
while (still_running);

根据需要使用CURLOPT_HEADERFUNCTION/CURLOPT_WRITEFUNCTION 回调将响应标头/有效负载保存到transfer 结构中,不要只是在CURLOPT_DEBUGFUNCTION 回调中将其转储到标准输出。这样,只有当最终响应代码是您要查找的内容时,您才能打印/保存数据。

【讨论】:

  • 这解决了,谢谢你的帮助。关于“使用CURLOPT_HEADERFUNCTION/CURLOPT_WRITEFUNCTION回调”,这些是否仍保留在setup(...)函数中,而不是像原始程序那样写入文件,我可以编写transfer结构?
  • @p.luck 是的,他们可以
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多