Libcurl HTTP

jerichou
2022-12-02 / 0 评论 / 17 阅读 / 正在检测是否收录...

关于libcurl的相关函数介绍以及参数详见官方说明
curl

lb6aj2h1.png

HTTP Request
一个http请求包含方法、路径、http版本、请求包头

请求方法 GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS

GET

curl_easy_setopt(curl, CURLOPT_URL, "http://example.com?id=1")

POST

curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "username=admin&password=123");

PUT

curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

DELETE

curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/file.txt");

libcurl HTTP 请求包头

默认不设置的包头

GET /file1.txt HTTP/1.1
Host: localhost
Accept: */*
POST /file1.txt HTTP/1.1
Host: localhost
Accept: */*
Content-Length: 6
Content-Type: application/x-www-form-urlencoded

设置自定义包头

添加一个包头 Name:Mike

struct curl_slist *list = NULL;
list = curl_slist_append(list, "Name: Mike");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* free the list again */

修改一个包头 Host: Alternative

struct curl_slist *list = NULL;
list = curl_slist_append(list, "Host: Alternative");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* free the list again */

删除一个包头

struct curl_slist *list = NULL;
list = curl_slist_append(list, "Accept:");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* free the list again */

提供一个没有内容的包头

struct curl_slist *list = NULL;
list = curl_slist_append(list, "Moo;");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* free the list again */

HTTP Response
HTTP response code

常见HTTP响应码

Code Meaning
1xx Transient code, a new one follows
2xx Things are OK
3xx The content is somewhere else
4xx Failed because of a client problem
5xx Failed because of a server problem
获取相应状态码

long code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
除了check curl请求的状态 check服务端返回过来的response statusCode

HTTP Range
如果客户端只希望从远程资源中获得前200个字节,或者在中间某个地方需要300个字节时

//只取前两百个字节
curl_easy_setopt(curl, CURLOPT_RANGE, "0-199");
 
//从索引200开始的文件中的所有内容
curl_easy_setopt(curl, CURLOPT_RANGE, "200-");
 
//从索引0获取200字节,从索引1000获取200字节
curl_easy_setopt(curl, CURLOPT_RANGE, "0-199,1000-199");

HTTP Authentication
用户名密码验证

curl_easy_setopt(curl, CURLOPT_USERNAME, "joe");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");

验证请求

客户端并不能决定发送一个验证请求,当服务有资源需要被保护并且需要请求验证,服务器将会返回一个401和WWW-Authenticate: header,这个header包含了特定的验证方式

curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE);

Cookie with libcurl
cookie验证

当启用cookie engine时 cookie将存储在相关联的句柄内存中

读取cookie

curl_easy_setopt(easy, CURLOPT_COOKIEFILE, "cookies.txt");

从服务端获取cookie并保存

curl_easy_setopt(easy, CURLOPT_COOKIEJAR, "cookies.txt");

稍后使用curl_easy_cleanup()关闭easy句柄时,所有已知的cookie将被写入给定文件。该文件格式是浏览器曾经使用过的众所周知的“ Netscape cookie文件”格式。

设置自定义cookie 不激活cookie engine

curl_easy_setopt(easy, CURLOPT_COOKIE, "name=daniel; present=yes;");

在其中设置的字符串是将在HTTP请求中发送的原始字符串,并且应采用NAME = VALUE重复序列的格式; 包括分号分隔符

cookie内存区存储大量cookie

Add a cookie to the cookie store

#define SEP  "\\t"  /* Tab separates the fields */
 
char *my_cookie =
  "example.com"    /* Hostname */
  SEP "FALSE"      /* Include subdomains */
  SEP "/"          /* Path */
  SEP "FALSE"      /* Secure */
  SEP "0"          /* Expiry in epoch time format. 0 == Session */
  SEP "foo"        /* Name */
  SEP "bar";       /* Value */
 
curl_easy_setopt(curl, CURLOPT_COOKIELIST, my_cookie);

Get all cookies from the cookie store

struct curl_slist *cookies
curl_easy_getinfo(easy, CURLINFO_COOKIELIST, &cookies);
/*这将返回指向cookie的链接列表的指针,并且每个cookie(再次)被指定为cookie文件格式的一行。该列表已分配给您,因此在应用程序处理完该信息后,请不要忘记调用curl_slist_free_all*/

Cookie store commands

//清除整个内存记录
curl_easy_setopt(curl, CURLOPT_COOKIELIST, "ALL");
 
//从内存中删除所有会话cookie(无有效期的cookie)
curl_easy_setopt(curl, CURLOPT_COOKIELIST, "SESS");
 
//强制将所有cookie写入先前使用CURLOPT_COOKIEJAR指定的文件名
curl_easy_setopt(curl, CURLOPT_COOKIELIST, "FLUSH");
 
//从先前用CURLOPT_COOKIEFILE指定的文件名强制重新加载cookie
curl_easy_setopt(curl, CURLOPT_COOKIELIST, "RELOAD");
Download

GET方法请求资源

easy = curl_easy_init();
curl_easy_setopt(easy, CURLOPT_URL, "http://example.com/");
curl_easy_perform(easy);

如果使用其他方法请求资源向切随后换回get请求

curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);

下载相应的返回包头数据 用CURLOPT_HEADER

easy = curl_easy_init();
curl_easy_setopt(easy, CURLOPT_HEADER, 1L);
curl_easy_setopt(easy, CURLOPT_URL, "http://example.com/");
curl_easy_perform(easy);

或者将包头数据存储到文件

easy = curl_easy_init();
FILE *file = fopen("headers", "wb");
curl_easy_setopt(easy, CURLOPT_HEADERDATA, file);
curl_easy_setopt(easy, CURLOPT_URL, "http://example.com/");
curl_easy_perform(easy);
fclose(file);

在开发时设置详细模式,将同时显示发送到stderr的传出和传入标头:

curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);

获取一个页面存储到内存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <curl/curl.h>
 
struct MemoryStruct {
  char *memory;
  size_t size;
};
 
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;
  mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
  if(mem->memory == NULL) {
    return 0;
  }
  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;
  return realsize;
}
 
int main(void)
{
  CURL *curl_handle;
  CURLcode res;
 
  struct MemoryStruct chunk;
 
  chunk.memory = malloc(1); 
  chunk.size = 0; 
 
  curl_global_init(CURL_GLOBAL_ALL); //整个程序全局只需要初始化一次 多次初始化会出问题
  curl_handle = curl_easy_init();
  curl_easy_setopt(curl_handle, CURLOPT_URL, "https://www.example.com/");
  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
  //curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
  res = curl_easy_perform(curl_handle);
  if(res != CURLE_OK) {
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(res));
  }
  else {
    printf("%lu bytes retrieved\n", (long)chunk.size);
  }
  curl_easy_cleanup(curl_handle);
 
  free(chunk.memory);
  curl_global_cleanup();
 
  return 0;
}

获取一个文件流的回调函数

//下载文件
static size_t
Writeback(void* pBuffer, size_t nSize, size_t nMemByte, void* pParam)
{
    //把下载到的数据以追加的方式写入文件(一定要有a,否则前面写入的内容就会被覆盖了)  
    FILE* fp = NULL;
    fopen_s(&fp, "E:\\test_1.wav", "ab+");
    size_t nWrite = fwrite(pBuffer, nSize, nMemByte, fp);
    fclose(fp);
    return nWrite;
}
//如果未设置此回调,则默认情况下libcurl使用'fwrite

Uploadload
http post

HTTP请求将数据传递到服务器的标准方法,使用libcurl,通常可以提供该数据作为指针和长度

curl_easy_setopt(easy, CURLOPT_POSTFIELDS, dataptr);
curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, (long)datalength);

使用自定义回调函数

将不断调用回调函数,直到所有数据都传输或者传输失败 如果未设置此回调,则默认情况下libcurl使用'fread'

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <curl/curl.h>
 
 
//回调函数的签名 必须按照这个签名实现自己的回调函数
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
  size_t retcode;
  curl_off_t nread;
  retcode = fread(ptr, size, nmemb, stream);
  nread = (curl_off_t)retcode;
  fprintf(stderr, "*** We read %" CURL_FORMAT_CURL_OFF_T
          " bytes from file\n", nread);
  return retcode;
}
 
int main(int argc, char **argv)
{
  CURL *curl;
  CURLcode res;
  FILE * hd_src;
  struct stat file_info;
 
  char *file;
  char *url;
 
  if(argc < 3)
    return 1;
 
  file = argv[1];
  url = argv[2];
 
  stat(file, &file_info);
 
  hd_src = fopen(file, "rb");
 
  curl_global_init(CURL_GLOBAL_ALL);
 
  curl = curl_easy_init();
  if(curl) {
    /* we want to use our own read function */ 
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
 
    /* enable uploading */ 
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
 
    /* HTTP PUT please */ 
    curl_easy_setopt(curl, CURLOPT_PUT, 1L);
 
    /* specify target URL, and note that this URL should include a file
       name, not only a directory */ 
    curl_easy_setopt(curl, CURLOPT_URL, url);
 
    /* now specify which file to upload */ 
    curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
 
    /* provide the size of the upload, we specicially typecast the value
       to curl_off_t since we must be sure to use the correct data size */ 
    curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
                     (curl_off_t)file_info.st_size);
 
    /* Now run off and do what you've been told! */ 
    res = curl_easy_perform(curl);
    /* Check for errors */ 
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));
 
    /* always cleanup */ 
    curl_easy_cleanup(curl);
  }
  fclose(hd_src); /* close the local file */ 
 
  curl_global_cleanup();
  return 0;
}

HTTP PUT

CURLOPT_INFILESIZE_LARGE设置上传的大小

curl_easy_setopt(easy, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(easy, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
curl_easy_setopt(easy, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(easy, CURLOPT_URL, "https://example.com/handle/put");
/*如果不知道在传输之前不知道传输的大小,对于HTTP 1.0,您必须事先提供大小,HTTP 1.1 你可以添加 标头 Transfer-Encoding: chunked 对于HTTP 2及更高版本,则既不需要大小也不需要额外的标头*/
1

评论 (0)

取消