С++ libcurl проверяет заголовок перед загрузкой тела одним запросом

В С++ я хочу использовать libcurl, чтобы проверить, что URL-адрес является текстовым/html, и если да, то затем загрузить тело, иначе он остановится.

Я хочу сделать это одним шагом, не отправляя сначала HEAD, если HEAD в порядке, снова запрашивая страницу для загрузки.

Если это невозможно с libcurl, предоставляет ли он другие библиотеки для C++, которые бы это поддерживали?


person user1015314    schedule 27.05.2012    source источник


Ответы (2)


Чтобы отправить запрос HTTP HEAD, вам нужно настроить эту опцию:

 curl_easy_setopt(ctx,CURLOPT_NOBODY ,1 );

и вы также можете взглянуть на этот вопрос: help требуется при программировании libcurl при отправке запроса HTTP HEAD

чтобы отделить заголовки от тела (не знаю, действительно ли это нужно), вы можете посмотреть:

http://curl.haxx.se/libcurl/c/sepheaders.html

чтобы загрузить множество разных URL-адресов libcurl-multi, вы должны взглянуть на:

http://curl.haxx.se/libcurl/c/libcurl-multi.html

небольшая модификация http://curl.haxx.se/libcurl/c/sendrecv.html :

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

/* Auxiliary function that waits on the socket. */
static int wait_on_socket(curl_socket_t sockfd, int for_recv, long timeout_ms)
{
  struct timeval tv;
  fd_set infd, outfd, errfd;
  int res;

  tv.tv_sec = timeout_ms / 1000;
  tv.tv_usec= (timeout_ms % 1000) * 1000;

  FD_ZERO(&infd);
  FD_ZERO(&outfd);
  FD_ZERO(&errfd);

  FD_SET(sockfd, &errfd); /* always check for error */

  if(for_recv)
  {
    FD_SET(sockfd, &infd);
  }
  else
  {
    FD_SET(sockfd, &outfd);
  }

  /* select() returns the number of signalled sockets or -1 */
  res = select(sockfd + 1, &infd, &outfd, &errfd, &tv);
  return res;
}

int main(void)
{
  CURL *curl;
  CURLcode res;
  /* Minimalistic http request */
  const char *request = "GET / HTTP/1.0\r\nHost: m0g.net\r\n\r\n";
  curl_socket_t sockfd; /* socket */
  long sockextr;
  size_t iolen;
  curl_off_t nread;

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "http://m0g.net");
    /* Do not do the transfer - only connect to host */
    curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
    res = curl_easy_perform(curl);

    if(CURLE_OK != res)
    {
      printf("Error: %s\n", strerror(res));
      return 1;
    }

    /* Extract the socket from the curl handle - we'll need it for waiting.
     * Note that this API takes a pointer to a 'long' while we use
     * curl_socket_t for sockets otherwise.
     */
    res = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockextr);

    if(CURLE_OK != res)
    {
      printf("Error: %s\n", curl_easy_strerror(res));
      return 1;
    }

    sockfd = sockextr;

    /* wait for the socket to become ready for sending */
    if(!wait_on_socket(sockfd, 0, 60000L))
    {
      printf("Error: timeout.\n");
      return 1;
    }

    puts("Sending request.");
    /* Send the request. Real applications should check the iolen
     * to see if all the request has been sent */
    res = curl_easy_send(curl, request, strlen(request), &iolen);

    if(CURLE_OK != res)
    {
      printf("Error: %s\n", curl_easy_strerror(res));
      return 1;
    }
    puts("Reading response.");
    char data[2048];
    int idxread=0;

    /* read the response */
    for(;;)
    {
      char buf[1024];

      wait_on_socket(sockfd, 1, 60000L);
      res = curl_easy_recv(curl, buf, 32, &iolen);

      if(CURLE_OK != res)
        break;

      if (nread+idxread > 2048)
          break;

      strncpy(data+idxread,buf,nread);
      idxread+=nread;

      if (strstr(data,"\r\n\r\n") != NULL) {
        if (strstr(data,"Content-Type: text/html") == NULL) {
            printf("not an html document.");
            return 2;
        }
      }

      nread = (curl_off_t)iolen;

      printf("Received %" CURL_FORMAT_CURL_OFF_T " bytes.\n", nread);
    }
    printf("'''%s'''\n", data);

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}
person zmo    schedule 27.05.2012
comment
хм, я хочу сделать это в одном запросе, а не в двух запросах. - person user1015314; 27.05.2012
comment
то, как вы хотите это сделать, не соответствует тому, как был разработан HTTP. Если вы выполняете запрос GET и читаете только заголовки, сервер все равно отправит вам весь контент, даже если вы откажетесь от него, закрыв файл fd. Поскольку заголовки обычно легкие, вы можете сначала отправить запрос HTTP HEAD, а затем повторно использовать сокет с помощью libcurl-multi для отправки запроса GET. - person zmo; 27.05.2012
comment
stackoverflow.com/questions/1849723/ проверьте здесь его php, но я хочу перейти на C++ - person user1015314; 27.05.2012
comment
о, на самом деле, для этой цели вам не нужен libcurl-multi, взгляните на curl.haxx.se/libcurl/c/sendrecv.html, вы можете извлечь fd из текущего запроса с помощью curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockextr); - person zmo; 27.05.2012
comment
и я думаю, что вы действительно можете взломать этот последний пример, чтобы отбросить данные в цикле /* read the response */, если тип содержимого не совпадает. - person zmo; 27.05.2012
comment
посмотрите на мое редактирование, оно должно делать то, что вы хотите. Конечно, вам придется улучшить его для ваших нужд. - person zmo; 27.05.2012

Это запоздало на несколько лет, но вот альтернативный способ сделать это, предложенный Линусом Нильсеном Фельтцингом, первоначальным автором cURL.

#include <curl/curl.h> 
int main(int argc, char *argv[]) 
{ 
   CURLcode ret; 
   CURL *hnd = curl_easy_init(); 
   curl_easy_setopt(hnd, CURLOPT_URL, "http://www.haxx.se"); 
   curl_easy_setopt(hnd, CURLOPT_HEADER, 1); 
   curl_easy_setopt(hnd, CURLOPT_NOBODY, 1); 
   ret = curl_easy_perform(hnd); 
   curl_easy_cleanup(hnd); 
}

Линус предложил это в ветке 2007 года в списке рассылки curl-library.

person Jules    schedule 30.05.2016