libcurl完全教程(注意:是,完全教程)

本文介绍使用libcurl进行网络请求的基本原则和方法,重点讲解C语言接口。内容涵盖编译配置、链接、SSL支持检查及常见HTTP操作技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文也是翻译官网的原文的,有些翻译不过来我的就用原文显示了

  • libcurl官网

  • 本文尝试描述使用libcurl编程时的一般原则和一些基本方法,文本将主要注意力集中在C接口。

  • 此文档将引用’the user‘作为编写使用libcurl的源代码的人。 这可能是你或你的位置的人。

  • 通常称’the program‘的是您收集的使用libcurl进行传输的源代码。 程序在libcurl之外,libcurl在程序之外。

  • 要获得此处描述的所有选项和功能的更多详细信息,请参阅其各自的手册页。


Building(生成)

There are many different ways to build C programs. This chapter will assume a Unix style build process. If you use a different build system, you can still read this to get general information that may apply to your environment as well.

有许多的方法来生成C语言的程序, 本章将假设一个Unix风格的生成过程。 如果使用不同的构建系统,您仍然可以阅读此信息,以获取可能适用于您的环境的一般信息。

(译者注:用C语言从编写源码,到运行一个程序的过程,可以概括为: 编写源代码(edit)-> 编译(compile)-> 连接,也叫生成(build)-> 运行(execute))


Compiling the Program(编译程序)

  • Your compiler needs to know where the libcurl headers are located. Therefore you must set your compiler’s include path to point to the directory where you installed them. The ‘curl-config’[3] tool can be used to get this information:

  • 你的编译器需要知道libcurl头在哪里。 因此,必须将编译器的include路径设置为指向安装它们的目录。 curl-config[3]工具可用于获取此信息:

$ curl-config --cflags 

Linking the Program with libcurl(链接程序到libcurl)

  • When having compiled the program, you need to link your object files to create a single executable. For that to succeed, you need to link with libcurl and possibly also with other libraries that libcurl itself depends on. Like the OpenSSL libraries, but even some standard OS libraries may be needed on the command line. To figure out which flags to use, once again the ‘curl-config’ tool comes to the rescue:

  • 编译程序时,需要链接目标文件以创建单个可执行文件。 为了成功,你需要链接到libcurl,也可能与libcurl本身依赖的其他库。 像OpenSSL库一样,但在命令行上甚至可能需要一些标准的操作系统库。 要找出要使用的标志,curl-config工具再次拯救:

$ curl-config --libs 

SSL or Not(使用SSL或者不)

  • libcurl can be built and customized in many ways. One of the things that varies from different libraries and builds is the support for SSL-based transfers, like HTTPS and FTPS. If a supported SSL library was detected properly at build-time, libcurl will be built with SSL support. To figure out if an installed libcurl has been built with SSL support enabled, use ‘curl-config’ like this:

  • libcurl可以在许多方面构建和定制。 从不同的库和构建不同的事情之一是支持基于SSL的数据传输,如HTTPSFTPS。 如果在构建时正确检测到受支持的SSL库,libcurl将使用SSL支持构建。 要确定是否已安装libcurl已启用SSL支持,请使用curl-config这样:

$ curl-config --feature
  • And if SSL is supported, the keyword ‘SSL’ will be written to stdout, possibly together with a few other features that could be either on or off on for different libcurls.

  • See also the “Features libcurl Provides” further down.

  • 如果支持SSL,关键字SSL将被写入stdout,可能有一些其他功能可以打开或关闭不同的libcurls

  • 另见下面的 特性libcurl提供。


autoconf macro(autoconf 宏 %只供参考%)

  • When you write your configure script to detect libcurl and setup variables accordingly, we offer a prewritten macro that probably does everything you need in this area. See docs/libcurl/libcurl.m4 file - it includes docs on how to use it.

  • 当您编写配置脚本以相应地检测libcurl和设置变量时,我们提供一个预写的宏,可以做你可能在这一领域需要的一切。 请参阅docs/libcurl/libcurl.m4文件 - 包括如何使用它的文档。


Portable Code in a Portable World(可移植的代码在可移植的世界)

The people behind libcurl have put a considerable effort to make libcurl work on a large amount of different operating systems and environments.

You program libcurl the same way on all platforms that libcurl runs on. There are only very few minor considerations that differ. If you just make sure to write your code portable enough, you may very well create yourself a very portable program. libcurl shouldn’t stop you from that.

libcurl背后的人已经付出了相当大的努力,使得libcurl可以在大量不同的操作系统和环境中工作。

你可以以相同的方式写含有libcurl库的代码在不同的平台上,然后libcurl可以运行。 只有很少的几率你需要考虑平台之间的不同。 只有你已经确保你的其他代码可移植性足够,libcurl不会成为阻止你的代码,你可以很好地创建自己的一个可移植程序。


Global Preparation(全面的准备 %只供参考%)

  • The program must initialize some of the libcurl functionality globally. That means it should be done exactly once, no matter how many times you intend to use the library. Once for your program’s entire life time. This is done using

  • 程序必须在全局上初始化一些libcurl功能。 这意味着它应该或者只应该完成一次,无论你打算使用lib多少次。 一次为你的程序的整个生命周期。 这是通过使用以下命令:

 curl_global_init()
  • and it takes one parameter which is a bit pattern that tells libcurl what to initialize. Using CURL_GLOBAL_ALL will make it initialize all known internal sub modules, and might be a good default option. The current two bits that are specified are:

  • 它需要一个参数,这是一个位模式告诉libcurl怎么初始化。 使用CURL_GLOBAL_ALL将使它初始化所有已知的内部子模块,这可能是一个好的默认选项(意思就是你最好用这个模块初始化所有的lib除非你真正知道你需要和初始化什么模块)。 指定的当前两位为:


CURL_GLOBAL_WIN32

  • which only does anything on Windows machines. When used on a Windows machine, it’ll make libcurl initialize the win32 socket stuff. Without having that initialized properly, your program cannot use sockets properly. You should only do this once for each application, so if your program already does this or of another library in use does it, you should not tell libcurl to do this as well.

  • 它只在Windows机器上做任何事情(就是这个函数它是用在Windows机器上的,如果你在Windows上写代码并编译就用它)。 当在Windows机器上使用时,它会使libcurl初始化win32套接字。 没有正确初始化,你的程序不能正确使用套接字。 你应该只为每个程序初始化一次,所以如果你的程序已经做到这一点或另一个库在使用它,你就不应该再让libcurl这样做了。


CURL_GLOBAL_SSL

  • which only does anything on libcurls compiled and built SSL-enabled. On these systems, this will make libcurl initialize the SSL library properly for this application. This only needs to be done once for each application so if your program or another library already does this, this bit should not be needed.

  • 它只对libcurls编译和构建启用SSL的任何内容。 在这些系统上,这将使libcurl为此应用程序正确初始化SSL库。 这只需要为每个应用程序执行一次,如果您的程序或另一个库已经这样做,这步骤可以不要。

  • libcurl has a default protection mechanism that detects if curl_global_init hasn’t been called by the time curl_easy_perform is called and if that is the case, libcurl runs the function itself with a guessed bit pattern. Please note that depending solely on this is not considered nice nor very good.

  • libcurl有一个默认保护机制,用于检测curl_easy_perform是否被调用,如果curl_global_init没有被调用,如果是这种情况,libcurl将以猜测的位模式运行函数本身。 请注意,这不被认为不错也不不被认为是很好。

  • When the program no longer uses libcurl, it should call curl_global_cleanup, which is the opposite of the init call. It will then do the reversed operations to cleanup the resources the curl_global_init call initialized.

  • 当程序不再使用libcurl时,它应该调用curl_global_cleanup,这是init调用的相反。 然后它将执行反向操作来清除curl_global_init调用初始化的资源。

  • Repeated calls to curl_global_init and curl_global_cleanup should be avoided. They should only be called once each.

  • 应避免重复调用curl_global_init和curl_global_cleanup。 它们应该只被调用一次。


Features libcurl Provides(libcurl所提供的特性)

It is considered best-practice to determine libcurl features at run-time rather than at build-time (if possible of course). By calling curl_version_info and checking out the details of the returned struct, your program can figure out exactly what the currently running libcurl supports.

最好的做法是在运行时而不是在构建时确定libcurl特性(如果可能的话)。 通过调用curl_version_info并检查返回的结构的详细信息,您的程序可以确定当前正在运行的libcurl支持什么。


Two Interfaces(两个接口)

libcurl first introduced the so called easy interface. All operations in the easy interface are prefixed with curl_easy. The easy interface lets you do single transfers with a synchronous and blocking function call.

libcurl首先介绍了所谓的easy interfaceeasy interface中的所有操作都以curl_easy作为前缀。 easy interface允许您使用同步和阻塞函数调用进行单次传输。

libcurl also offers another interface that allows multiple simultaneous transfers in a single thread, the so called multi interface. More about that interface is detailed in a separate chapter further down. You still need to understand the easy interface first, so please continue reading for better understanding.

libcurl还提供了另一个接口,允许在单个线程中进行多个同时传输,即所谓的多接口(multi interface)。 关于该接口的更多内容将在下面单独的章节中详细说明。 您仍然需要先了解easy interface,因此请继续阅读以便更好地了解。


Handle the Easy libcurl(处理简单libcurl)

To use the easy interface, you must first create yourself an easy handle. You need one handle for each easy session you want to perform. Basically, you should use one handle for every thread you plan to use for transferring. You must never share the same handle in multiple threads.

Get an easy handle with

要使用easy interface,您必须首先创建一个简单的句柄(easy handle)。 你需要为每个简单的会话你想要执行一个句柄。 基本上,您应该为计划用于传输的每个线程使用一个句柄。 您不能在多个线程中共享相同的句柄(译者注:这点你想共享handle句柄的话,可以查看

easyhandle = curl_easy_init();

It returns an easy handle. Using that you proceed to the next step: setting up your preferred actions. A handle is just a logic entity for the upcoming transfer or series of transfers.

它返回一个容易的句柄。 使用它,继续下一步:设置首选操作。 句柄只是用于即将到来的传送或一系列传送的逻辑实体。

You set properties and options for this handle using curl_easy_setopt. They control how the subsequent transfer or transfers will be made. Options remain set in the handle until set again to something different. They are sticky. Multiple requests using the same handle will use the same options.

您可以使用curl_easy_setopt为此句柄设置属性和选项。 他们控制如何进行后续转移。 选项保持设置在句柄中,直到设置为不同的东西。 他们是粘的。 使用相同句柄的多个请求将使用相同的选项。

If you at any point would like to blank all previously set options for a single easy handle, you can call curl_easy_reset and you can also make a clone of an easy handle (with all its set options) using curl_easy_duphandle.

如果你在任何时候想要为一个简单的句柄清除所有以前设置的选项,你可以调用curl_easy_reset,你也可以使用curl_easy_duphandle克隆一个简单句柄(所有的设置选项)。

Many of the options you set in libcurl are “strings”, pointers to data terminated with a zero byte. When you set strings with curl_easy_setopt, libcurl makes its own copy so that they don’t need to be kept around in your application after being set[4].

libcurl中设置的许多选项是”strings”(字符串),指向以零字节终止的数据的指针。 当你使用curl_easy_setopt设置字符串时,libcurl会创建自己的副本,因此在设置之后,它们不需要保存在应用程序中。

One of the most basic properties to set in the handle is the URL. You set your preferred URL to transfer with CURLOPT_URL in a manner similar to:

在句柄中设置的最基本的属性之一是URL。 您使用CURLOPT_URL设置要传输的首选网址,方法与以下类似:

curl_easy_setopt(handle, CURLOPT_URL, "http://domain.com/");

Let’s assume for a while that you want to receive data as the URL identifies a remote resource you want to get here. Since you write a sort of application that needs this transfer, I assume that you would like to get the data passed to you directly instead of simply getting it passed to stdout. So, you write your own function that matches this prototype:

让我们假设您希望接收数据,因为URL标识了您要在此处访问的远程资源。 因为你写了一个需要这种传输的应用程序,我假设你想直接获得传递给你的数据,而不是简单地传递给stdout(也就是你想自己的函数处理这些返回的数据,而不是显示在屏幕上(stdout))。 所以,你编写自己的函数匹配这个原型:

size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp); 

You tell libcurl to pass all data to this function by issuing a function similar to this:

你使用类似于这样的函数告诉libcurl传递所有数据到这个函数(这是curl初始化设置时候做的):

curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data); 

You can control what data your callback function gets in the fourth argument by setting another property:

您可以通过设置另一个属性来控制回调函数在<第四个参数>中获取的数据:

 curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &internal_struct); 

Using that property, you can easily pass local data between your application and the function that gets invoked by libcurl. libcurl itself won’t touch the data you pass with CURLOPT_WRITEDATA.

使用该属性,您可以轻松地在应用程序和libcurl调用的函数之间传递本地数据。 libcurl本身不会接触你通过CURLOPT_WRITEDATA传递的数据。

libcurl offers its own default internal callback that will take care of the data if you don’t set the callback with CURLOPT_WRITEFUNCTION. It will then simply output the received data to stdout. You can have the default callback write the data to a different file handle by passing a ‘FILE *’ to a file opened for writing with the CURLOPT_WRITEDATA option.

如果不使用CURLOPT_WRITEFUNCTION设置回调,libcurl会提供自己的默认内部回调来处理数据。 然后它将简单地将接收到的数据输出到stdout。 您可以通过将FILE *传递到使用CURLOPT_WRITEDATA选项打开的文件,使默认回调将数据写入不同的文件句柄。

Now, we need to take a step back and have a deep breath. Here’s one of those rare platform-dependent nitpicks. Did you spot it? On some platforms[2], libcurl won’t be able to operate on files opened by the program. Thus, if you use the default callback and pass in an open file with CURLOPT_WRITEDATA, it will crash. You should therefore avoid this to make your program run fine virtually everywhere.

现在,我们需要退后一步,深呼吸。 这里有一个罕见的平台相关的nitpicks。 你找到了吗? 在某些平台[2]上,libcurl无法操作由程序打开的文件。 因此,如果您使用默认回调并传递一个打开的文件给CURLOPT_WRITEDATA,它将崩溃。 因此,您应该避免这样做,使您的程序几乎无处不在运行。

(CURLOPT_WRITEDATA was formerly known as CURLOPT_FILE. Both names still work and do the same thing).

CURLOPT_WRITEDATA以前称为CURLOPT_FILE。这两个名称仍然工作,并做同样的事情)。

If you’re using libcurl as a win32 DLL, you MUST use the CURLOPT_WRITEFUNCTION if you set CURLOPT_WRITEDATA - or you will experience crashes.

如果你使用libcurl就像win32的DLL一样,你必须使用CURLOPT_WRITEFUNCTION,如果你设置CURLOPT_WRITEDATA - 或者你会遇到崩溃。

There are of course many more options you can set, and we’ll get back to a few of them later. Let’s instead continue to the actual transfer:

当然还有更多的选择,你可以设置,我们将回到其中几个后来。 让我们继续实际传输:

success = curl_easy_perform(easyhandle); 

curl_easy_perform will connect to the remote site, do the necessary commands and receive the transfer. Whenever it receives data, it calls the callback function we previously set. The function may get one byte at a time, or it may get many kilobytes at once. libcurl delivers as much as possible as often as possible. Your callback function should return the number of bytes it “took care of”. If that is not the exact same amount of bytes that was passed to it, libcurl will abort the operation and return with an error code.

curl_easy_perform将连接到远程站点,执行必要的命令并接收传输。每当它接收数据,它调用我们以前设置的回调函数。该函数可以一次获得一个字节,或者它可以一次获得许多千字节。 libcurl尽可能多地提供尽可能多的。你的回调函数应该返回”你所关心的”的字节数。如果这不是传递给它的完全相同的字节量,libcurl将中止操作并返回一个错误代码。

When the transfer is complete, the function returns a return code that informs you if it succeeded in its mission or not. If a return code isn’t enough for you, you can use the CURLOPT_ERRORBUFFER to point libcurl to a buffer of yours where it’ll store a human readable error message as well.

传输完成后,函数返回一个返回码,通知您是否成功完成任务。如果一个返回代码不够,你可以使用CURLOPT_ERRORBUFFER将libcurl指向你的缓冲区,它将存储一个人可读的错误消息。

If you then want to transfer another file, the handle is ready to be used again. Mind you, it is even preferred that you re-use an existing handle if you intend to make another transfer. libcurl will then attempt to re-use the previous connection.

如果随后要传输另一个文件,则该句柄已准备好再次使用。注意,如果您打算再次进行传输,最好重新使用现有的句柄。 libcurl然后将尝试重新使用先前的连接。

For some protocols, downloading a file can involve a complicated process of logging in, setting the transfer mode, changing the current directory and finally transferring the file data. libcurl takes care of all that complication for you. Given simply the URL to a file, libcurl will take care of all the details needed to get the file moved from one machine to another.

对于一些协议,下载文件可能涉及登录的复杂过程,设置传送模式,改变当前目录并最终传送文件数据。 libcurl照顾所有那些复杂的你。给定一个文件的URL,libcurl将处理所有需要的细节,以使文件从一台机器移动到另一台机器。


Multi-threading Issues(多线程问题)

libcurl is thread safe but there are a few exceptions. Refer to libcurl-thread for more information.

libcurl是线程安全的,但有一些例外。 有关更多信息,请参阅libcurl-thread。


When It Doesn’t Work

There will always be times when the transfer fails for some reason. You might have set the wrong libcurl option or misunderstood what the libcurl option actually does, or the remote server might return non-standard replies that confuse the library which then confuses your program.

总有一些时候,由于某种原因传输失败。你可能设置了错误的libcurl选项或误解了libcurl选项实际上做了什么,或者远程服务器可能返回非标准的回复混淆库,然后混淆你的程序。

There’s one golden rule when these things occur: set the CURLOPT_VERBOSE option to 1. It’ll cause the library to spew out the entire protocol details it sends, some internal info and some received protocol data as well (especially when using FTP). If you’re using HTTP, adding the headers in the received output to study is also a clever way to get a better understanding why the server behaves the way it does. Include headers in the normal body output with CURLOPT_HEADER set 1.

当这些事情发生时,有一个黄金规则:将CURLOPT_VERBOSE选项设置为1.它将使得库显示它发送的整个协议细节,一些内部信息和一些接收的协议数据(特别是当使用FTP时)。如果你使用HTTP,在接收到的输出中添加头部来学习也是一个聪明的方式来更好地了解服务器为什么会这样做。在CURLOPT_HEADER设置为1的正常正文输出中包括标头。

Of course, there are bugs left. We need to know about them to be able to fix them, so we’re quite dependent on your bug reports! When you do report suspected bugs in libcurl, please include as many details as you possibly can: a protocol dump that CURLOPT_VERBOSE produces, library version, as much as possible of your code that uses libcurl, operating system name and version, compiler name and version etc.

当然,还有bug。我们需要知道他们能够修复它们,所以我们完全依赖于你的错误报告!当您在libcurl中报告可疑错误时,请尽可能包括尽可能多的细节:CURLOPT_VERBOSE生成的协议转储,库版本,尽可能多的使用libcurl的代码,操作系统名称和版本,编译器名称和版本等等

If CURLOPT_VERBOSEis not enough, you increase the level of debug data your application receive by using the CURLOPT_DEBUGFUNCTION.

如果CURLOPT_VERBOSE不够,您可以使用CURLOPT_DEBUGFUNCTION增加应用程序接收的调试数据的级别。

Getting some in-depth knowledge about the protocols involved is never wrong, and if you’re trying to do funny things, you might very well understand libcurl and how to use it better if you study the appropriate RFC documents at least briefly.

获得关于所涉及的协议的一些深入的知识是从来没有错,如果你想做有趣的事情,你可能很好地理解libcurl和如何使用它更好,如果你学习适当的RFC文档至少简要。


Upload Data to a Remote Site(将数据上传到远程站点)

libcurl tries to keep a protocol independent approach to most transfers, thus uploading to a remote FTP site is very similar to uploading data to a HTTP server with a PUT request.

libcurl尝试保持大多数传输的协议独立的方法,因此上传到远程FTP站点非常类似于使用PUT请求将数据上传到HTTP服务器

Of course, first you either create an easy handle or you re-use one existing one. Then you set the URL to operate on just like before. This is the remote URL, that we now will upload.

当然,首先你要么创建一个简单的句柄,要么重新使用一个现有的句柄。 然后你设置URL的操作就像以前一样。 这是远程URL,我们现在将上传。

Since we write an application, we most likely want libcurl to get the upload data by asking us for it. To make it do that, we set the read callback and the custom pointer libcurl will pass to our read callback. The read callback should have a prototype similar to:

由于我们编写一个应用程序,我们最有可能希望libcurl通过请求我们获取上传数据。 为了做到这一点,我们设置read回调,自定义指针libcurl将传递给我们的read回调。 读回调应该有类似的原型:

 size_t function(char *bufptr, size_t size, size_t nitems, void *userp); 

Where bufptr is the pointer to a buffer we fill in with data to upload and size*nitems is the size of the buffer and therefore also the maximum amount of data we can return to libcurl in this call. The userp pointer is the custom pointer we set to point to a struct of ours to pass private data between the application and the callback.

其中bufptr是指向缓冲区的指针,我们填充要上传的数据,size * nitems是缓冲区的大小,因此也是我们可以在此调用中返回libcurl的最大数据量。 userp指针是我们设置为指向我们的结构体的自定义指针,以在应用程序和回调之间传递私有数据。

curl_easy_setopt(easyhandle, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(easyhandle, CURLOPT_READDATA, &filedata); 

Tell libcurl that we want to upload:

告诉libcurl我们想上传文件:

curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, 1L); 

A few protocols won’t behave properly when uploads are done without any prior knowledge of the expected file size. So, set the upload file size using the CURLOPT_INFILESIZE_LARGE for all known file sizes like this[1]:

在完成上传时,没有预期文件大小的任何预先知识,一些协议将无法正常工作。 所以,使用CURLOPT_INFILESIZE_LARGE为所有已知的文件大小设置上传文件大小,如[1]:

/* in this example, file_size must be an curl_off_t variable */
/* 在本例中,文件大小必须是curl_off_t类型的变量 */
curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE_LARGE, file_size);

When you call curl_easy_perform this time, it’ll perform all the necessary operations and when it has invoked the upload it’ll call your supplied callback to get the data to upload. The program should return as much data as possible in every invoke, as that is likely to make the upload perform as fast as possible. The callback should return the number of bytes it wrote in the buffer. Returning 0 will signal the end of the upload.

当你这次调用curl_easy_perform时,它会执行所有必要的操作,当它调用上传时,它会调用你提供的回调来获取数据上传。 程序应该在每次调用中返回尽可能多的数据,因为这可能会使上传执行尽可能快。 回调应该返回它在缓冲区中写入的字节数。 返回0将表示上传结束。


Passwords

Many protocols use or even require that user name and password are provided to be able to download or upload the data of your choice. libcurl offers several ways to specify them.

许多协议使用或甚至要求提供用户名和密码,以便能够下载或上传您选择的数据。 libcurl提供了几种方法来指定它们。

Most protocols support that you specify the name and password in the URL itself. libcurl will detect this and use them accordingly. This is written like this:

大多数协议支持您在URL本身中指定名称和密码。 libcurl将检测到并相应地使用它们。 这是这样写的:

protocol://user:password@example.com/path/

If you need any odd letters in your user name or password, you should enter them URL encoded, as %XX where XX is a two-digit hexadecimal number.

如果您在用户名或密码中需要任何奇怪字母,则应输入URL编码,如%XX,其中XX是两位十六进制数(译者注:网址的传输并不是以明文方式,需要一定的编码将明文转换成base64或者其他的什么编码才能传输)。

libcurl also provides options to set various passwords. The user name and password as shown embedded in the URL can instead get set with the CURLOPT_USERPWD option. The argument passed to libcurl should be a char * to a string in the format “user:password”. In a manner like this:

libcurl还提供了设置各种密码的选项。 如图所示,嵌入在URL中的用户名和密码可以使用CURLOPT_USERPWD选项设置。 传递给libcurl的参数应该是一个char *,格式为user:password。 以这样的方式:

curl_easy_setopt(easyhandle, CURLOPT_USERPWD, "myname:thesecret"); 

Another case where name and password might be needed at times, is for those users who need to authenticate themselves to a proxy they use. libcurl offers another option for this, the CURLOPT_PROXYUSERPWD. It is used quite similar to the CURLOPT_USERPWD option like this:

有时可能需要名称和密码的另一种情况是那些需要通过他们使用的代理对自己进行身份验证的用户。 libcurl为此提供了另一个选项,CURLOPT_PROXYUSERPWD。 它的使用非常类似于CURLOPT_USERPWD选项,像这样:

 curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "myname:thesecret"); 

There’s a long time Unix “standard” way of storing FTP user names and passwords, namely in the $HOME/.netrc file. The file should be made private so that only the user may read it (see also the “Security Considerations” chapter), as it might contain the password in plain text. libcurl has the ability to use this file to figure out what set of user name and password to use for a particular host. As an extension to the normal functionality, libcurl also supports this file for non-FTP protocols such as HTTP. To make curl use this file, use the CURLOPT_NETRC option:

有很长一段时间是使用Unix”标准”的方式来存储FTP用户名和密码,即在$HOME/.netrc文件中。 该文件应该是私有的,以便只有用户可以阅读它(参见“安全注意事项”一章),因为它可能包含明文密码。 libcurl有能力使用这个文件来找出用于特定主机的用户名和密码的集合。 作为正常功能的扩展,libcurl还支持非FTP协议(如HTTP)的此文件。 要使curl使用此文件,请使用CURLOPT_NETRC选项:

curl_easy_setopt(easyhandle, CURLOPT_NETRC, 1L); 

And a very basic example of how such a .netrc file may look like:
这里有个很基础的例子来展示.netrc文件看起像什么:

machine myhost.mydomain.com
login userlogin
password secretword

All these examples have been cases where the password has been optional, or at least you could leave it out and have libcurl attempt to do its job without it. There are times when the password isn’t optional, like when you’re using an SSL private key for secure transfers.

To pass the known private key password to libcurl:

所有这些例子都是密码是可选的,或者至少你可以离开它,并有libcurl尝试没有它的工作。 有时,密码不是可选的,例如,当您使用SSL私钥进行安全传输时。

将已知的私钥密码传递给libcurl:

curl_easy_setopt(easyhandle, CURLOPT_KEYPASSWD, "keypassword"); 

HTTP Authentication(HTTP验证)

The previous chapter showed how to set user name and password for getting URLs that require authentication. When using the HTTP protocol, there are many different ways a client can provide those credentials to the server and you can control which way libcurl will (attempt to) use them. The default HTTP authentication method is called ‘Basic’, which is sending the name and password in clear-text in the HTTP request, base64-encoded. This is insecure.

上一章展示了如何设置用于获取需要身份验证的URL的用户名和密码。 当使用HTTP协议时,客户端可以通过许多不同的方式向服务器提供这些凭据,并且您可以控制libcurl将(尝试)使用它们的方式。 默认的HTTP认证方法叫做“Basic”,它以HTTP请求中的明文形式发送名称和密码,base64编码。 这是不安全的。

At the time of this writing, libcurl can be built to use: Basic, Digest, NTLM, Negotiate (SPNEGO). You can tell libcurl which one to use with CURLOPT_HTTPAUTH as in:

在撰写本文时,libcurl可以构建为使用:BasicDigestNTLMNegotiate(SPNEGO)。 你可以告诉libcurl使用哪个CURLOPT_HTTPAUTH

 curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 

And when you send authentication to a proxy, you can also set authentication type the same way but instead with CURLOPT_PROXYAUTH:

当您向代理发送身份验证时,您还可以使用相同的方式设置身份验证类型,但使用CURLOPT_PROXYAUTH

curl_easy_setopt(easyhandle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM); 

Both these options allow you to set multiple types (by ORing them together), to make libcurl pick the most secure one out of the types the server/proxy claims to support. This method does however add a round-trip since libcurl must first ask the server what it supports:

这两个选项允许您设置多个类型(通过对它们进行ORing化),使libcurl选择服务器/代理声称支持的类型中最安全的一个。 然而,此方法添加一个往返,因为libcurl必须首先询问服务器它支持:

curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC); 

For convenience, you can use the CURLAUTH_ANY define (instead of a list with specific types) which allows libcurl to use whatever method it wants.

为了方便起见,可以使用CURLAUTH_ANY定义(而不是具有特定类型的列表),它允许libcurl使用它想要的任何方法。

When asking for multiple types, libcurl will pick the available one it considers “best” in its own internal order of preference.

当要求多个类型时,libcurl将选择它在自己的内部优先顺序中认为“最好”的可用类型。


HTTP POSTing(HTTP 发布操作(POST)ing)

We get many questions regarding how to issue HTTP POSTs with libcurl the proper way. This chapter will thus include examples using both different versions of HTTP POST that libcurl supports.

我们得到许多关于如何使用libcurl正确的方式发出HTTP POST的问题。 因此本章将包括使用libcurl支持的不同版本的HTTP POST的示例。

The first version is the simple POST, the most common version, that most HTML pages using the tag uses. We provide a pointer to the data and tell libcurl to post it all to the remote site:

第一个版本是简单的POST,最常见的版本,大多数HTML页面使用标签使用。 我们提供一个指向数据的指针,并告诉libcurl将其全部发布到远程站点:

char *data="name=daniel&project=curl";
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(easyhandle, CURLOPT_URL, "http://posthere.com/");

curl_easy_perform(easyhandle); /* post away! */

Simple enough, huh? Since you set the POST options with the CURLOPT_POSTFIELDS, this automatically switches the handle to use POST in the upcoming request.

够简单,是吗? 由于您使用CURLOPT_POSTFIELDS设置POST选项,因此会自动将句柄切换到在即将到来的请求中使用POST。

Ok, so what if you want to post binary data that also requires you to set the Content-Type: header of the post? Well, binary posts prevent libcurl from being able to do strlen() on the data to figure out the size, so therefore we must tell libcurl the size of the post data. Setting headers in libcurl requests are done in a generic way, by building a list of our own headers and then passing that list to libcurl.

Ok,所以如果你想发布二进制数据,还需要你设置Content-Type:标题的帖子? 好吧,二进制帖子阻止libcurl对数据做strlen()操作,以确定大小,因此我们必须告诉libcurl的post数据的大小。 在libcurl请求中设置头文件是通过一个通用的方法,通过构建一个我们自己的头文件列表,然后将列表传递给libcurl。

/* 定义一个curl_slist类型的结构指针命名为headers */
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: text/xml");

/* post binary data 发布二进制数据 */
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, binaryptr);

/* set the size of the postfields data 设置发布数据的大小 */
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDSIZE, 23L);

/* pass our list of custom made headers 发布我们的列表用自定义的http头部 */
curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);

curl_easy_perform(easyhandle); /* post away! */

curl_slist_free_all(headers); /* free the header list 释放http头部 */

While the simple examples above cover the majority of all cases where HTTP POST operations are required, they don’t do multi-part formposts. Multi-part formposts were introduced as a better way to post (possibly large) binary data and were first documented in the RFC 1867 (updated in RFC 2388). They’re called multi-part because they’re built by a chain of parts, each part being a single unit of data. Each part has its own name and contents. You can in fact create and post a multi-part formpost with the regular libcurl POST support described above, but that would require that you build a formpost yourself and provide to libcurl. To make that easier, libcurl provides curl_formadd. Using this function, you add parts to the form. When you’re done adding parts, you post the whole form.

虽然上面的简单示例涵盖了需要HTTP POST操作的大多数情况,但它们不会执行多部分表单。 多部分表单被引入作为更好的方式来发布(可能是大的)二进制数据,并且首先记录在RFC 1867(在RFC 2388中更新)。 它们被称为多部分,因为它们由一系列部分构成,每个部分是单个数据单元。 每个部分都有自己的名称和内容。 事实上,您可以使用上面描述的常规libcurl POST支持来创建和发布多部分表单,但是这需要您自己构建一个formpost并提供给libcurl。 为了使这更容易,libcurl提供curl_formadd。 使用此功能,您可以向表单中添加零件。 当您完成添加零件后,您发布整个表单。

The following example sets two simple text parts with plain textual contents, and then a file with binary contents and uploads the whole thing.

以下示例使用纯文本内容设置两个简单文本部分,然后使用二进制内容设置文件,并上传整个内容。

struct curl_httppost *post=NULL;
struct curl_httppost *last=NULL;
curl_formadd(&post, &last,
             CURLFORM_COPYNAME, "name",
             CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
curl_formadd(&post, &last,
             CURLFORM_COPYNAME, "project",
             CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
curl_formadd(&post, &last,
             CURLFORM_COPYNAME, "logotype-image",
             CURLFORM_FILECONTENT, "curl.png", CURLFORM_END);

/* Set the form info */
curl_easy_setopt(easyhandle, CURLOPT_HTTPPOST, post);

curl_easy_perform(easyhandle); /* post away! */

/* free the post data again */
curl_formfree(post);

Multipart formposts are chains of parts using MIME-style separators and headers. It means that each one of these separate parts get a few headers set that describe the individual content-type, size etc. To enable your application to handicraft this formpost even more, libcurl allows you to supply your own set of custom headers to such an individual form part. You can of course supply headers to as many parts as you like, but this little example will show how you set headers to one specific part when you add that to the post handle:

多部分表单是使用MIME样式分隔符和标题的部分链。 这意味着这些单独的部分中的每一个都获得一些描述单个内容类型,大小等的头设置。为了使您的应用程序能够更多地工作这个formpost,libcurl允许您提供自己的一套自定义头文件 个别形式部分。 你当然可以提供尽可能多的部分,但这个小例子将显示如何设置标题到一个特定的部分,当你添加到post句柄:

struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: text/xml");

curl_formadd(&post, &last,
             CURLFORM_COPYNAME, "logotype-image",
             CURLFORM_FILECONTENT, "curl.xml",
             CURLFORM_CONTENTHEADER, headers,
             CURLFORM_END);
 curl_easy_perform(easyhandle); /* post away! */

curl_formfree(post); /* free post */
curl_slist_free_all(headers); /* free custom header list */

Since all options on an easyhandle are “sticky”, they remain the same until changed even if you do call curl_easy_perform, you may need to tell curl to go back to a plain GET request if you intend to do one as your next request. You force an easyhandle to go back to GET by using the CURLOPT_HTTPGET option:

因为easyhandle上的所有选项都是“sticky”,它们保持不变直到改变,即使你调用curl_easy_perform,你可能需要告诉curl回到一个简单的GET请求,如果你打算做一个作为下一个请求。 使用CURLOPT_HTTPGET选项强制简单句柄返回GET:

 curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1L);

Just setting CURLOPT_POSTFIELDS to "" or NULL will not stop libcurl from doing a POST. It will just make it POST without any data to send!

只要将CURLOPT_POSTFIELDS设置为""NULL将不会停止libcurl做POST。 它将只是使其POST没有任何数据发送!


Showing Progress(显示进展)

For historical and traditional reasons, libcurl has a built-in progress meter that can be switched on and then makes it present a progress meter in your terminal.

由于历史和传统的原因,libcurl有一个内置的进度表,可以打开,然后使它呈现在您的终端进度表。

Switch on the progress meter by, oddly enough, setting CURLOPT_NOPROGRESS to zero. This option is set to 1 by default.

奇怪的是,打开进度表,将CURLOPT_NOPROGRESS设置为零。 默认情况下,此选项设置为1。

For most applications however, the built-in progress meter is useless and what instead is interesting is the ability to specify a progress callback. The function pointer you pass to libcurl will then be called on irregular intervals with information about the current transfer.

然而,对于大多数应用程序,内置的进度表是无用的,而有趣的是能够指定进度回调。 然后,传递给libcurl的函数指针将在有关当前传输的信息的不规则间隔上调用。

Set the progress callback by using CURLOPT_PROGRESSFUNCTION. And pass a pointer to a function that matches this prototype:

使用CURLOPT_PROGRESSFUNCTION设置进度回调。 并传递一个指针到匹配这个原型的函数:

nt progress_callback(void *clientp,
                     double dltotal,
                     double dlnow,
                     double ultotal,
                     double ulnow);

If any of the input arguments is unknown, a 0 will be passed. The first argument, the clientp is the pointer you pass to libcurl with CURLOPT_PROGRESSDATA. libcurl won’t touch it.

如果任何输入参数未知,将传递0。 第一个参数,clientp是您传递给libcurl与CURLOPT_PROGRESSDATA的指针。 libcurl不会碰它。

libcurl with C++

There’s basically only one thing to keep in mind when using C++ instead of C when interfacing libcurl:

The callbacks CANNOT be non-static class member functions

Example C++ code:

在连接libcurl时使用C ++而不是C时,基本上只有一件事要注意:

回调不能是非静态类成员函数

示例C ++代码:

class AClass {
    static size_t write_data(void *ptr, size_t size, size_t nmemb,
                             void *ourpointer)
    {
      /* do what you want with the data */
    }
 }

Proxies(代理)

What “proxy” means according to Merriam-Webster: “a person authorized to act for another” but also “the agency, function, or office of a deputy who acts as a substitute for another”.

根据Merriam-Webster的说法,“代理人”是指“被授权为另一人行事的人”,而且“代理人代理人的代理人,职能或办公室”。

Proxies are exceedingly common these days. Companies often only offer Internet access to employees through their proxies. Network clients or user-agents ask the proxy for documents, the proxy does the actual request and then it returns them.

代理是非常普遍的这些天。公司通常只通过代理人为员工提供互联网接入。网络客户端或用户代理向代理请求文档,代理执行实际请求,然后返回它们。

libcurl supports SOCKS and HTTP proxies. When a given URL is wanted, libcurl will ask the proxy for it instead of trying to connect to the actual host identified in the URL.

libcurl支持SOCKS和HTTP代理。当需要给定的URL时,libcurl将询问代理,而不是尝试连接到URL中标识的实际主机。

If you’re using a SOCKS proxy, you may find that libcurl doesn’t quite support all operations through it.

如果你使用SOCKS代理,你可能会发现libcurl不支持所有的操作。

For HTTP proxies: the fact that the proxy is a HTTP proxy puts certain restrictions on what can actually happen. A requested URL that might not be a HTTP URL will be still be passed to the HTTP proxy to deliver back to libcurl. This happens transparently, and an application may not need to know. I say “may”, because at times it is very important to understand that all operations over a HTTP proxy use the HTTP protocol. For example, you can’t invoke your own custom FTP commands or even proper FTP directory listings.

对于HTTP代理:代理是HTTP代理的事实对实际可能发生的事情施加了某些限制。可能不是HTTP URL的请求的URL仍将被传递到HTTP代理以传回libcurl。这是透明的,应用可能不需要知道。我说“可以”,因为有时,非常重要的是要理解,通过HTTP代理的所有操作使用HTTP协议。例如,您不能调用您自己的自定义FTP命令或甚至适当的FTP目录列表。


Proxy Options

  • To tell libcurl to use a proxy at a given port number:

  • 告诉libcurl在给定端口号使用代理:

curl_easy_setopt(easyhandle, CURLOPT_PROXY, "proxy-host.com:8080"); 
  • Some proxies require user authentication before allowing a request, and you pass that information similar to this:

  • 一些代理在允许请求之前需要用户身份验证,您传递的信息类似于:

curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "user:password"); 
  • If you want to, you can specify the host name only in the CURLOPT_PROXY option, and set the port number separately with CURLOPT_PROXYPORT.

  • 如果需要,您只能在CURLOPT_PROXY选项中指定主机名,并使用CURLOPT_PROXYPORT分别设置端口号。

  • Tell libcurl what kind of proxy it is with CURLOPT_PROXYTYPE (if not, it will default to assume a HTTP proxy):

  • 告诉libcurl它是什么样的代理它是与CURLOPT_PROXYTYPE(如果不是,它将默认假设一个HTTP代理):

curl_easy_setopt(easyhandle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); 

Environment Variables(环境变量)

  • libcurl automatically checks and uses a set of environment variables to know what proxies to use for certain protocols. The names of the variables are following an ancient de facto standard and are built up as "[protocol]_proxy"(note the lower casing). Which makes the variable 'http_proxy' checked for a name of a proxy to use when the input URL is HTTP. Following the same rule, the variable named 'ftp_proxy' is checked for FTP URLs. Again, the proxies are always HTTP proxies, the different names of the variables simply allows different HTTP proxies to be used.

  • libcurl自动检查并使用一组环境变量来了解某些协议使用什么代理。变量的名称遵循一个古老的事实标准,并被建立为[protocol] _proxy(注意下套管)。这使得变量“http_proxy”在输入URL是HTTP时检查要使用的代理的名称。遵循相同的规则,检查名为“ftp_proxy”的变量的FTP URL。同样,代理总是HTTP代理,变量的不同名称只允许使用不同的HTTP代理。

  • The proxy environment variable contents should be in the format "[protocol://][user:password@]machine[:port]". Where the protocol:// part is simply ignored if present (so http://proxy and bluerk://proxy will do the same) and the optional port number specifies on which port the proxy operates on the host. If not specified, the internal default port number will be used and that is most likely not the one you would like it to be.

  • 代理环境变量内容应为格式"[protocol//][user:password@]machine[:port]"。其中protocol://部分被忽略(如果存在)(因此http:// proxy和bluerk:// proxy将会这样做),可选端口号指定代理在主机上在哪个端口上操作。如果没有指定,将使用内部默认端口号,这很可能不是你想要的。

  • There are two special environment variables.'all_proxy'is what sets proxy for any URL in case the protocol specific variable wasn’t set, and 'no_proxy' defines a list of hosts that should not use a proxy even though a variable may say so. If 'no_proxy' is a plain asterisk (“*”) it matches all hosts.

  • 有两个特殊的环境变量。 'all_proxy'是在未设置协议特定变量的情况下为任何URL设置代理,'no_proxy'定义不应使用代理的主机列表,即使变量可能这样。如果'no_proxy'是一个简单的星号("*"),它匹配所有主机。

  • To explicitly disable libcurl’s checking for and using the proxy environment variables, set the proxy name to "" - an empty string - with CURLOPT_PROXY.

  • 要显式禁用libcurl的检查和使用代理环境变量,请将代理名称设置为""- 一个空字符串 - 使用CURLOPT_PROXY


SSL and Proxies(SSL和代理)

  • SSL is for secure point-to-point connections. This involves strong encryption and similar things, which effectively makes it impossible for a proxy to operate as a “man in between” which the proxy’s task is, as previously discussed. Instead, the only way to have SSL work over a HTTP proxy is to ask the proxy to tunnel trough everything without being able to check or fiddle with the traffic.

  • SSL用于安全的点对点连接。这涉及强加密和类似的事情,这有效地使得代理不可能作为代理的任务是“之间的人”操作,如先前所讨论的。相反,使SSL通过HTTP代理工作的唯一方法是请求代理通过所有通道,而不能检查或干扰流量。

  • Opening an SSL connection over a HTTP proxy is therefor a matter of asking the proxy for a straight connection to the target host on a specified port. This is made with the HTTP request CONNECT. (“please mr proxy, connect me to that remote host”).

  • 通过HTTP代理打开SSL连接就是要求代理在指定端口上直接连接到目标主机。这是使用HTTP请求CONNECT。(“please mr proxy, connect me to that remote host”)。

  • Because of the nature of this operation, where the proxy has no idea what kind of data that is passed in and out through this tunnel, this breaks some of the very few advantages that come from using a proxy, such as caching. Many organizations prevent this kind of tunneling to other destination port numbers than 443 (which is the default HTTPS port number).

  • 由于此操作的本质,代理不知道通过此隧道传入和传出哪种类型的数据,这打破了使用代理(如缓存)带来的一些非常少的优点。许多组织防止这种类型的隧道到其他目标端口号443(这是默认的HTTPS端口号)。


Tunneling Through Proxy(通过代理隧道)

  • As explained above, tunneling is required for SSL to work and often even restricted to the operation intended for SSL; HTTPS.

  • 如上所述,SSL需要隧道来工作,并且通常甚至限于用于SSL的操作; HTTPS。

  • This is however not the only time proxy-tunneling might offer benefits to you or your application.

然而,这不是代理隧道可能为您或您的应用程序提供好处的唯一时间。

  • As tunneling opens a direct connection from your application to the remote machine, it suddenly also re-introduces the ability to do non-HTTP operations over a HTTP proxy. You can in fact use things such as FTP upload or FTP custom commands this way.

  • 由于隧道打开从应用程序到远程机器的直接连接,它突然也重新引入了通过HTTP代理执行非HTTP操作的能力。 你可以使用FTP上传或FTP自定义命令这样的方式。

  • Again, this is often prevented by the administrators of proxies and is rarely allowed.

  • 再次,这通常由代理的管理员阻止,并且很少被允许。

  • Tell libcurl to use proxy tunneling like this:

  • 告诉libcurl使用这样的代理隧道:

curl_easy_setopt(easyhandle, CURLOPT_HTTPPROXYTUNNEL, 1L); 
  • In fact, there might even be times when you want to do plain HTTP operations using a tunnel like this, as it then enables you to operate on the remote server instead of asking the proxy to do so. libcurl will not stand in the way for such innovative actions either!

  • 事实上,甚至可能有时你想使用这样的隧道进行纯HTTP操作,因为它允许你在远程服务器上操作,而不是要求代理这样做。 libcurl不会阻碍这种创新的行动!


Proxy Auto-Config(代理自动配置)

  • Netscape first came up with this. It is basically a web page (usually using a .pac extension) with a Javascript that when executed by the browser with the requested URL as input, returns information to the browser on how to connect to the URL. The returned information might be "DIRECT" (which means no proxy should be used), "PROXY host:port" (to tell the browser where the proxy for this particular URL is) or "SOCKS host:port" (to direct the browser to a SOCKS proxy).

  • Netscape首先想出了这个。它基本上是一个带有Javascript的网页(通常使用.pac扩展名),当被浏览器以请求的URL作为输入执行时,将向浏览器返回关于如何连接到URL的信息。返回的信息可能是"DIRECT"(这意味着不应使用代理),"PROXY host:port"(告诉浏览器此特定URL的代理在哪里)或"SOCKS host:port"(指向浏览器到SOCKS代理)。

  • libcurl has no means to interpret or evaluate Javascript and thus it doesn’t support this. If you get yourself in a position where you face this nasty invention, the following advice have been mentioned and used in the past:

  • libcurl没有办法解释或评估Javascript,因此它不支持这一点。如果你让自己在一个你面对这种讨厌的发明的位置,以下的建议已经被提及和使用在过去:

    • Depending on the Javascript complexity, write up a script that translates it to another language and execute that.

    • 根据Javascript的复杂性,写一个脚本,将其翻译为另一种语言并执行。

    • 阅读Javascript代码并用另一种语言重写相同的逻辑。

    • Implement a Javascript interpreter; people have successfully used the Mozilla Javascript engine in the past.

    • 实现一个Javascript解释器;人们在过去成功地使用了Mozilla Javascript引擎。

    • Ask your admins to stop this, for a static proxy setup or similar.

    • 要求您的管理员停止此操作,进行静态代理设置或类似操作。


Persistence Is The Way to Happiness(坚持是幸福的道路)

Re-cycling the same easy handle several times when doing multiple requests is the way to go.

当做多个请求时,重复循环相同的简单句柄几次是要走的路。

After each single curl_easy_perform operation, libcurl will keep the connection alive and open. A subsequent request using the same easy handle to the same host might just be able to use the already open connection! This reduces network impact a lot.

在每个单个curl_easy_perform操作后,libcurl将保持连接活动并打开。后续请求对同一主机使用相同的简单句柄可能只能使用已经打开的连接!这减少了很多网络影响。

Even if the connection is dropped, all connections involving SSL to the same host again, will benefit from libcurl’s session ID cache that drastically reduces re-connection time.

即使连接断开,所有涉及到同一主机的SSL的连接也将受益于libcurl的会话ID缓存,这大大减少了重新连接时间。

FTP connections that are kept alive save a lot of time, as the command- response round-trips are skipped, and also you don’t risk getting blocked without permission to login again like on many FTP servers only allowing N persons to be logged in at the same time.

保持活动的FTP连接节省了大量时间,因为命令响应往返被跳过,并且您不会冒没有许可再次登录的风险,像在许多FTP服务器上只允许N个人登录与此同时。

libcurl caches DNS name resolving results, to make lookups of a previously looked up name a lot faster.

libcurl缓存DNS名称解析结果,以便查找以前查找的名称更快。

Other interesting details that improve performance for subsequent requests may also be added in the future.

也可以在将来添加用于改进后续请求的性能的其他有趣细节。

Each easy handle will attempt to keep the last few connections alive for a while in case they are to be used again. You can set the size of this “cache” with the CURLOPT_MAXCONNECTS option. Default is 5. There is very seldom any point in changing this value, and if you think of changing this it is often just a matter of thinking again.

每个简单的句柄将试图保持最后几个连接活动一段时间,以防它们被再次使用。您可以使用CURLOPT_MAXCONNECTS选项设置此“缓存”的大小。默认是5.在改变这个值很少有任何点,如果你想改变这个,它通常只是一个思考的问题。

To force your upcoming request to not use an already existing connection (it will even close one first if there happens to be one alive to the same host you’re about to operate on), you can do that by setting CURLOPT_FRESH_CONNECT to 1. In a similar spirit, you can also forbid the upcoming request to be “lying” around and possibly get re-used after the request by setting CURLOPT_FORBID_REUSE to 1.

强制你的即将来临的请求不使用已经存在的连接(如果恰好有一个连接到你要操作的同一个主机,它甚至会关闭一个),你可以通过设置CURLOPT_FRESH_CONNECT为1。类似的精神,你也可以禁止即将到来的请求“躺”,并可能在请求后重新使用通过设置CURLOPT_FORBID_REUSE为1。


HTTP Headers Used by libcurl(libcurl使用的HTTP头)

When you use libcurl to do HTTP requests, it’ll pass along a series of headers automatically. It might be good for you to know and understand these. You can replace or remove them by using the CURLOPT_HTTPHEADER option.

当你使用libcurl做HTTP请求时,它会自动传递一系列的头。 它可能有益于你知道和理解这些。 您可以使用CURLOPT_HTTPHEADER选项替换或删除它们。

Host

  • This header is required by HTTP 1.1 and even many 1.0 servers and should be the name of the server we want to talk to. This includes the port number if anything but default.

  • 这个头是HTTP 1.1和甚至许多1.0服务器所必需的,并且应该是我们要谈论的服务器的名称。 这包括端口号,如果除了默认值。

Accept
  • /“.
Expect
  • When doing POST requests, libcurl sets this header to “100-continue” to ask the server for an “OK” message before it proceeds with sending the data part of the post. If the POSTed data amount is deemed “small”, libcurl will not use this header.

  • 当执行POST请求时,libcurl将此标头设置为“100-continue”,以便在继续发送帖子的数据部分之前向服务器询问“OK”消息。 如果POSTed数据量被认为是“小”,libcurl将不使用这个头。


Customizing Operations(自定义操作)

There is an ongoing development today where more and more protocols are built upon HTTP for transport. This has obvious benefits as HTTP is a tested and reliable protocol that is widely deployed and has excellent proxy-support.

今天有一个持续的发展,越来越多的协议基于HTTP进行传输。 这有明显的好处,因为HTTP是一种经过测试和可靠的协议,被广泛部署并具有出色的代理支持。

When you use one of these protocols, and even when doing other kinds of programming you may need to change the traditional HTTP (or FTP or…) manners. You may need to change words, headers or various data.

当您使用这些协议之一时,甚至在进行其他类型的编程时,您可能需要更改传统的HTTP(或FTP或…)方式。 您可能需要更改字词,标题或各种数据。

libcurl is your friend here too.

libcurl也是你的朋友。


CUSTOMREQUEST(自定义请求)

  • If just changing the actual HTTP request keyword is what you want, like when GET, HEAD or POST is not good enough for you, CURLOPT_CUSTOMREQUEST is there for you. It is very simple to use:

  • 如果只是改变实际的HTTP请求关键字是你想要的,像GET,HEAD或POST不够好,CURLOPT_CUSTOMREQUEST是为你。 使用非常简单:

curl_easy_setopt(easyhandle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST");
  • When using the custom request, you change the request keyword of the actual request you are performing. Thus, by default you make a GET request but you can also make a POST operation (as described before) and then replace the POST keyword if you want to. You’re the boss.

  • 使用自定义请求时,您可以更改要执行的实际请求的请求关键字。 因此,默认情况下,您提出一个GET请求,但您也可以进行POST操作(如前所述),然后替换POST关键字,如果你想。 你是老板。

Modify Headers(修改标题)

  • HTTP-like protocols pass a series of headers to the server when doing the request, and you’re free to pass any amount of extra headers that you think fit. Adding headers is this easy:

  • 类HTTP协议在执行请求时向服务器传递一系列头,并且您可以自由传递任何您认为合适的额外头。 添加标题很简单:

struct curl_slist *headers=NULL; /* init to NULL is important */

headers = curl_slist_append(headers, "Hey-server-hey: how are you?");
headers = curl_slist_append(headers, "X-silly-content: yes");

/* pass our list of custom made headers */
curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);

curl_easy_perform(easyhandle); /* transfer http */

curl_slist_free_all(headers); /* free the header list */
  • … and if you think some of the internally generated headers, such as Accept: or Host: don’t contain the data you want them to contain, you can replace them by simply setting them too:

  • …如果你认为一些内部生成的头部,如Accept:或Host:不包含你想要它们包含的数据,你可以通过简单地设置它们来替换它们:

headers = curl_slist_append(headers, "Accept: Agent-007");
headers = curl_slist_append(headers, "Host: munged.host.line");

Delete Headers(删除标题)

  • If you replace an existing header with one with no contents, you will prevent the header from being sent. For instance, if you want to completely prevent the “Accept:” header from being sent, you can disable it with code similar to this:

  • 如果将现有标题替换为没有内容的标题,则将阻止发送标题。 例如,如果要完全阻止“Accept:”标头被发送,您可以使用类似于下面的代码禁用它:

headers = curl_slist_append(headers, "Accept:");
  • Both replacing and canceling internal headers should be done with careful consideration and you should be aware that you may violate the HTTP protocol when doing so.

  • 替换和取消内部标头应该仔细考虑,你应该知道,你可能违反HTTP协议这样做。

Enforcing chunked transfer-encoding(执行分块传输编码)

  • By making sure a request uses the custom header "Transfer-Encoding: chunked" when doing a non-GET HTTP operation, libcurl will switch over to "chunked" upload, even though the size of the data to upload might be known. By default, libcurl usually switches over to chunked upload automatically if the upload data size is unknown.

  • 通过确保在执行非GET HTTP操作时请求使用自定义标题"Transfer-Encoding:chunked",libcurl将切换到"chunked"上传,即使可能知道要上传的数据的大小。 默认情况下,如果上传数据大小未知,libcurl通常会自动切换到分块上传。

HTTP Version(HTTP版本)

  • All HTTP requests includes the version number to tell the server which version we support. libcurl speaks HTTP 1.1 by default. Some very old servers don’t like getting 1.1-requests and when dealing with stubborn old things like that, you can tell libcurl to use 1.0 instead by doing something like this:

  • 所有HTTP请求都包括版本号,告诉服务器我们支持哪个版本。 libcurl默认说HTTP 1.1。 一些非常老的服务器不喜欢获取1.1请求,当处理这种顽固的旧东西时,你可以告诉libcurl使用1.0:

curl_easy_setopt(easyhandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 

FTP Custom Commands(FTP自定义命令)

  • Not all protocols are HTTP-like, and thus the above may not help you when you want to make, for example, your FTP transfers to behave differently.

  • 并不是所有的协议都是HTTP的,因此上面的例子可能不能帮助你,例如,你的FTP传输的行为不同。

  • Sending custom commands to a FTP server means that you need to send the commands exactly as the FTP server expects them (RFC959 is a good guide here), and you can only use commands that work on the control-connection alone. All kinds of commands that require data interchange and thus need a data-connection must be left to libcurl’s own judgement. Also be aware that libcurl will do its very best to change directory to the target directory before doing any transfer, so if you change directory (with CWD or similar) you might confuse libcurl and then it might not attempt to transfer the file in the correct remote directory.

  • 向FTP服务器发送自定义命令意味着您需要完全按照FTP服务器的期望发送命令(RFC959是一个很好的指南),您只能使用在控制连接上工作的命令。 所有需要数据交换并因此需要数据连接的命令必须由libcurl自己判断。 还要注意,libcurl将做最好的目标到目标目录之前做任何传输,所以如果你改变目录(使用CWD或类似的),你可能会混淆libcurl,然后它可能不会尝试传输文件在正确 远程目录。

  • A little example that deletes a given file before an operation:

  • 在操作之前删除给定文件的一个示例:

headers = curl_slist_append(headers, "DELE file-to-remove");

/* pass the list of custom commands to the handle */
curl_easy_setopt(easyhandle, CURLOPT_QUOTE, headers);

curl_easy_perform(easyhandle); /* transfer ftp data! */

curl_slist_free_all(headers); /* free the header list */
  • If you would instead want this operation (or chain of operations) to happen _after_ the data transfer took place the option to curl_easy_setopt would instead be calledCURLOPT_POSTQUOTE and used the exact same way.

  • 如果您希望此操作(或操作链)发生_after_数据传输发生,curl_easy_setopt的选项将改为称为CURLOPT_POSTQUOTE,并使用完全相同的方式。

  • The custom FTP command will be issued to the server in the same order they are added to the list, and if a command gets an error code returned back from the server, no more commands will be issued and libcurl will bail out with an error code (CURLE_QUOTE_ERROR). Note that if you use CURLOPT_QUOTE to send commands before a transfer, no transfer will actually take place when a quote command has failed.

  • 自定义FTP命令将以与添加到列表中的顺序相同的顺序发送到服务器,如果命令获取从服务器返回的错误代码,则不会再发出任何命令,libcurl将会释放出错代码 (CURLE_QUOTE_ERROR)。 请注意,如果您在传输之前使用CURLOPT_QUOTE发送命令,则当quote命令失败时,实际上不会进行传输。

  • If you set the CURLOPT_HEADER to 1, you will tell libcurl to get information about the target file and output “headers” about it. The headers will be in “HTTP-style”, looking like they do in HTTP.

  • 如果将CURLOPT_HEADER设置为1,您将告诉libcurl获取有关目标文件的信息,并输出有关它的“headers”。 标头将是“HTTP风格”,看起来像他们在HTTP中。

  • The option to enable headers or to run custom FTP commands may be useful to combine with CURLOPT_NOBODY. If this option is set, no actual file content transfer will be performed.

  • 启用头或运行自定义FTP命令的选项可能有助于与CURLOPT_NOBODY组合。 如果设置了此选项,则不会执行实际的文件内容传输。

FTP Custom CUSTOMREQUEST(FTP自定义请求)

  • If you do want to list the contents of a FTP directory using your own defined FTP command, CURLOPT_CUSTOMREQUEST will do just that. "NLST"is the default one for listing directories but you’re free to pass in your idea of a good alternative.

  • 如果你想使用你自己定义的FTP命令列出FTP目录的内容,CURLOPT_CUSTOMREQUEST就会这么做。 "NLST"是列出目录的默认值,但你可以自由地传递一个好的替代品的想法。


Cookies Without Chocolate Chips(巧克力片的巧克力曲奇饼 %什么鬼%)

In the HTTP sense, a cookie is a name with an associated value. A server sends the name and value to the client, and expects it to get sent back on every subsequent request to the server that matches the particular conditions set. The conditions include that the domain name and path match and that the cookie hasn’t become too old.

在HTTP意义上,cookie是具有关联值的名称。服务器向客户端发送名称和值,并且期望在服务器的每个匹配所设置的特定条件的后续请求时发送该名称和值。条件包括域名和路径匹配,并且cookie不会变得太旧。

In real-world cases, servers send new cookies to replace existing ones to update them. Server use cookies to “track” users and to keep “sessions”.

在现实情况下,服务器会发送新的Cookie以替换现有Cookie以进行更新。服务器使用cookie来“跟踪”用户和保持“会话”。

Cookies are sent from server to clients with the header Set-Cookie: and they’re sent from clients to servers with the Cookie: header.

Cookie从服务器发送到具有标头Set-Cookie的客户端:并且它们从客户端发送到具有Cookie:头的服务器。

To just send whatever cookie you want to a server, you can use CURLOPT_COOKIE to set a cookie string like this:

要将任何您想要的cookie发送到服务器,您可以使用CURLOPT_COOKIE设置一个cookie字符串,如下所示:

curl_easy_setopt(easyhandle, CURLOPT_COOKIE, "name1=var1; name2=var2;");

In many cases, that is not enough. You might want to dynamically save whatever cookies the remote server passes to you, and make sure those cookies are then used accordingly on later requests.

在许多情况下,这是不够的。您可能希望动态保存远程服务器传递给您的任何Cookie,并确保随后在后续请求中使用这些Cookie。

One way to do this, is to save all headers you receive in a plain file and when you make a request, you tell libcurl to read the previous headers to figure out which cookies to use. Set the header file to read cookies from with CURLOPT_COOKIEFILE.

一种方法是将所有接收到的头文件保存在一个普通文件中,当你提出请求时,你告诉libcurl读取以前的头文件来确定要使用的cookies。设置头文件以从CURLOPT_COOKIEFILE读取Cookie。

The CURLOPT_COOKIEFILE option also automatically enables the cookie parser in libcurl. Until the cookie parser is enabled, libcurl will not parse or understand incoming cookies and they will just be ignored. However, when the parser is enabled the cookies will be understood and the cookies will be kept in memory and used properly in subsequent requests when the same handle is used. Many times this is enough, and you may not have to save the cookies to disk at all. Note that the file you specify to CURLOPT_COOKIEFILE doesn’t have to exist to enable the parser, so a common way to just enable the parser and not read any cookies is to use the name of a file you know doesn’t exist.

CURLOPT_COOKIEFILE选项也会自动启用libcurl中的cookie解析器。直到启用cookie解析器,libcurl将不会解析或理解传入的cookie,它们将被忽略。然而,当解析器被启用时,cookie将被理解,并且cookie将被保存在内存中,并在使用相同句柄时在后续请求中正确使用。很多时候,这是足够的,你可能不必保存cookie到磁盘。请注意,指定给CURLOPT_COOKIEFILE的文件不必存在以启用解析器,因此只需启用解析器而不读取任何Cookie的常见方法是使用您知道的文件名称不存在。

If you would rather use existing cookies that you’ve previously received with your Netscape or Mozilla browsers, you can make libcurl use that cookie file as input. The CURLOPT_COOKIEFILE is used for that too, as libcurl will automatically find out what kind of file it is and act accordingly.

如果您希望使用以前在Netscape或Mozilla浏览器中收到的现有Cookie,可以让libcurl使用该cookie文件作为输入。 CURLOPT_COOKIEFILE也用于这个,因为libcurl会自动找出它是什么样的文件,并采取相应的行动。

Perhaps the most advanced cookie operation libcurl offers, is saving the entire internal cookie state back into a Netscape/Mozilla formatted cookie file. We call that the cookie-jar. When you set a file name with CURLOPT_COOKIEJAR, that file name will be created and all received cookies will be stored in it when curl_easy_cleanup is called. This enables cookies to get passed on properly between multiple handles without any information getting lost.

也许libcurl提供的最高级的cookie操作是将整个内部cookie状态保存回Netscape / Mozilla格式的cookie文件。我们称之为饼干罐。当您使用CURLOPT_COOKIEJAR设置文件名时,将会创建该文件名,并且在调用curl_easy_cleanup时,所有接收到的cookie都将存储在其中。这使得cookie可以在多个句柄之间正确传递,而不会丢失任何信息。


FTP Peculiarities We Need(FTP特性我们需要)

FTP transfers use a second TCP/IP connection for the data transfer. This is usually a fact you can forget and ignore but at times this fact will come back to haunt you. libcurl offers several different ways to customize how the second connection is being made.

FTP传输使用第二个TCP / IP连接进行数据传输。这通常是一个事实,你可以忘记和忽略,但有时这个事实会回来困扰你。 libcurl提供了几种不同的方式来定制如何进行第二个连接。

libcurl can either connect to the server a second time or tell the server to connect back to it. The first option is the default and it is also what works best for all the people behind firewalls, NATs or IP-masquerading setups. libcurl then tells the server to open up a new port and wait for a second connection. This is by default attempted with EPSV first, and if that doesn’t work it tries PASV instead. (EPSV is an extension to the original FTP spec and does not exist nor work on all FTP servers.)

libcurl可以连接到服务器第二次或告诉服务器连接回它。第一个选项是默认值,它也适用于防火墙,NAT或IP伪装设置后面的所有人。 libcurl然后告诉服务器打开一个新的端口并等待第二个连接。这是默认尝试与EPSV第一,如果这不工作,它尝试PASV。 (EPSV是原始FTP规范的扩展,不存在,也不在所有FTP服务器上工作。)

You can prevent libcurl from first trying the EPSV command by setting CURLOPT_FTP_USE_EPSV to zero.

您可以通过将CURLOPT_FTP_USE_EPSV设置为零来防止libcurl首先尝试EPSV命令

In some cases, you will prefer to have the server connect back to you for the second connection. This might be when the server is perhaps behind a firewall or something and only allows connections on a single port. libcurl then informs the remote server which IP address and port number to connect to. This is made with the CURLOPT_FTPPORT option. If you set it to "-", libcurl will use your system’s “default IP address”. If you want to use a particular IP, you can set the full IP address, a host name to resolve to an IP address or even a local network interface name that libcurl will get the IP address from.

在某些情况下,您更喜欢让服务器连接回您的第二个连接。这可能是当服务器可能在防火墙或某物之后,并且只允许在单个端口上的连接。 libcurl然后通知远程服务器要连接到哪个IP地址和端口号。这是使用CURLOPT_FTPPORT选项。如果将其设置为"- ",libcurl将使用您系统的“默认IP地址”。如果要使用特定的IP,您可以设置完整的IP地址,主机名解析为IP地址,甚至是libcurl将获取IP地址的本地网络接口名称。

When doing the "PORT" approach, libcurl will attempt to use the EPRT and the LPRT before trying PORT, as they work with more protocols. You can disable this behavior by setting CURLOPT_FTP_USE_EPRT to zero.

当执行"PORT"方法时,libcurl将尝试在尝试PORT之前使用EPRT和LPRT,因为它们使用更多的协议。您可以通过将CURLOPT_FTP_USE_EPRT设置为零来禁用此行为。


Headers Equal Fun(标题等同乐趣)

Some protocols provide “headers”, meta-data separated from the normal data. These headers are by default not included in the normal data stream, but you can make them appear in the data stream by setting CURLOPT_HEADER to 1.

一些协议提供“标题”,元数据与正常数据分离。 默认情况下,这些头不包括在普通数据流中,但您可以通过将CURLOPT_HEADER设置为1来使它们出现在数据流中。

What might be even more useful, is libcurl’s ability to separate the headers from the data and thus make the callbacks differ. You can for example set a different pointer to pass to the ordinary write callback by setting CURLOPT_HEADERDATA.

更有用的是,libcurl能够从数据中分离头,从而使回调不同。 你可以通过设置CURLOPT_HEADERDATA来设置一个不同的指针传递给普通的写回调。

Or, you can set an entirely separate function to receive the headers, by using CURLOPT_HEADERFUNCTION.

或者,您可以使用CURLOPT_HEADERFUNCTION设置一个完全独立的函数来接收头。

The headers are passed to the callback function one by one, and you can depend on that fact. It makes it easier for you to add custom header parsers etc.

头文件一个接一个地传递给回调函数,你可以依赖这个事实。 它使您更容易添加自定义头解析器等。

“Headers” for FTP transfers equal all the FTP server responses. They aren’t actually true headers, but in this case we pretend they are! ;-)

用于FTP传输的“报头”等于所有FTP服务器响应。 他们实际上不是真正的标题,但在这种情况下,我们假装他们是! ;-)


Post Transfer Information(转发信息)

[ curl_easy_getinfo ]


Security Considerations

The libcurl project takes security seriously. The library is written with caution and precautions are taken to mitigate many kinds of risks encountered while operating with potentially malicious servers on the Internet. It is a powerful library, however, which allows application writers to make trade offs between ease of writing and exposure to potential risky operations. If used the right way, you can use libcurl to transfer data pretty safely.

libcurl项目认真对待安全性。图书馆是谨慎编写的,并采取预防措施以减轻在互联网上潜在恶意服务器操作时遇到的各种风险。然而,它是一个强大的库,它允许应用程序编写者在易于写作和暴露于潜在的风险操作之间进行权衡。如果使用正确的方式,可以使用libcurl来安全地传输数据。

Many applications are used in closed networks where users and servers can be trusted, but many others are used on arbitrary servers and are fed input from potentially untrusted users. Following is a discussion about some risks in the ways in which applications commonly use libcurl and potential mitigations of those risks. It is by no means comprehensive, but shows classes of attacks that robust applications should consider. The Common Weakness Enumeration project at https://cwe.mitre.org/ is a good reference for many of these and similar types of weaknesses of which application writers should be aware.

许多应用程序在封闭网络中使用,其中用户和服务器可以受信任,但是许多其他应用程序在任意服务器上使用,并从可能不受信任的用户提供输入。以下是对应用程序通常使用libcurl和潜在缓解这些风险的方式中的一些风险的讨论。它不是全面的,但显示了强大的应用程序应该考虑的攻击类型。 https://cwe.mitre.org/上的Common Weakness Enumeration项目是许多应用程序作者应该意识到的这些类似弱点的很好的参考。

Command Lines(命令行)

  • If you use a command line tool (such as curl) that uses libcurl, and you give options to the tool on the command line those options can very likely get read by other users of your system when they use ‘ps’ or other tools to list currently running processes.

  • 如果使用使用libcurl的命令行工具(例如curl),并且在命令行上向工具提供选项,这些选项很可能被系统的其他用户使用ps或其他工具时读取 列出当前正在运行的进程。

  • To avoid this problem, never feed sensitive things to programs using command line options. Write them to a protected file and use the -K option to avoid this.

  • 为了避免这个问题,切勿使用命令行选项向程序提供敏感内容。 将它们写入受保护的文件,并使用-K选项来避免这种情况。

.netrc

  • .netrc is a pretty handy file/feature that allows you to login quickly and automatically to frequently visited sites. The file contains passwords in clear text and is a real security risk. In some cases, your .netrc is also stored in a home directory that is NFS mounted or used on another network based file system, so the clear text password will fly through your network every time anyone reads that file!

  • .netrc是一个非常方便的文件/功能,允许您快速,自动地登录常访问的网站。 该文件包含明文密码,是真正的安全风险。 在某些情况下,.netrc也存储在NFS安装的主目录中,或者在另一个基于网络的文件系统上使用,因此每次有人读取该文件时,明文密码都将通过您的网络传输!

  • To avoid this problem, don’t use .netrc files and never store passwords in plain text anywhere.

  • 要避免此问题,请不要使用.netrc文件,并且不要在任何地方以纯文本格式存储密码。

Clear Text Passwords(清除文本密码)

  • Many of the protocols libcurl supports send name and password unencrypted as clear text (HTTP Basic authentication, FTP, TELNET etc). It is very easy for anyone on your network or a network nearby yours to just fire up a network analyzer tool and eavesdrop on your passwords. Don’t let the fact that HTTP Basic uses base64 encoded passwords fool you. They may not look readable at a first glance, but they very easily “deciphered” by anyone within seconds.

  • 许多协议libcurl支持发送名称和密码未加密作为明文(HTTP基本认证,FTP,TELNET等)。 这是很容易的任何人在你的网络或附近的网络只是启动网络分析仪工具和窃听您的密码。 不要让事实,HTTP基本使用base64编码的密码骗了你。 他们可能不会看起来可读的乍一看,但他们很容易“解密”任何人在几秒钟内。

  • To avoid this problem, use an authentication mechanism or other protocol that doesn’t let snoopers see your password: Digest, CRAM-MD5, Kerberos, SPNEGO or NTLM authentication, HTTPS, FTPS, SCP and SFTP are a few examples.

  • 为避免此问题,请使用不让窥探者看到您的密码的身份验证机制或其他协议:Digest,CRAM-MD5,Kerberos,SPNEGO或NTLM身份验证,HTTPS,FTPS,SCP和SFTP是几个示例。

Redirects(重定向)

  • The CURLOPT_FOLLOWLOCATION option automatically follows HTTP redirects sent by a remote server. These redirects can refer to any kind of URL, not just HTTP. By default libcurl will allow all protocols on redirect except several disabled for security reasons: Since 7.19.4 FILE and SCP are disabled, and since 7.40.0 SMB and SMBS are also disabled.

  • CURLOPT_FOLLOWLOCATION选项自动跟随远程服务器发送的HTTP重定向。这些重定向可以引用任何类型的网址,而不仅仅是HTTP。默认情况下libcurl将允许所有协议重定向,除非几个被禁用,出于安全原因:因为7.19.4 FILE和SCP被禁用,并且从7.40.0 SMB和SMBS也被禁用。

  • A redirect to a file: URL would cause the libcurl to read (or write) arbitrary files from the local filesystem. If the application returns the data back to the user (as would happen in some kinds of CGI scripts), an attacker could leverage this to read otherwise forbidden data (e.g. file://localhost/etc/passwd).

  • 重定向到文件:URL将导致libcurl从本地文件系统读取(或写入)任意文件。如果应用程序将数据返回给用户(如在某些CGI脚本中发生的情况),攻击者可以利用此来读取其他禁止的数据(例如file://localhost/etc/passwd)。

  • If authentication credentials are stored in the ~/.netrc file, or Kerberos is in use, any other URL type (not just file:) that requires authentication is also at risk. A redirect such as ftp://some-internal-server/private-file would then return data even when the server is password protected.

  • 如果认证凭证存储在~/.netrc文件中,或Kerberos正在使用中,则需要认证的任何其他URL类型(而不仅仅是文件:)也存在风险。一个重定向,如ftp://some-internal-server/private-file将返回数据,即使服务器被密码保护。

  • In the same way, if an unencrypted SSH private key has been configured for the user running the libcurl application, SCP: or SFTP: URLs could access password or private-key protected resources, e.g. sftp://user@some-internal-server/etc/passwd

  • 以相同的方式,如果为运行libcurl应用程序的用户配置了未加密的SSH私钥,SCP:或SFTP:URL可以访问密码或私钥保护的资源,例如:sftp://user@some-internal-server/etc/passwd

  • The CURLOPT_REDIR_PROTOCOLS and CURLOPT_NETRC options can be used to mitigate against this kind of attack.

  • CURLOPT_REDIR_PROTOCOLSCURLOPT_NETRC选项可用于减轻此类攻击

  • A redirect can also specify a location available only on the machine running libcurl, including servers hidden behind a firewall from the attacker. e.g. http://127.0.0.1/ or http://intranet/delete-stuff.cgi?delete=all or tftp://bootp-server/pc-config-data

  • 重定向还可以指定仅在运行libcurl的机器上可用的位置,包括从攻击者隐藏在防火墙后面的服务器。例如http://127.0.0.1/http://intranet/delete-stuff.cgi?delete=all or tftp://bootp-server/pc-config-data

  • Apps can mitigate against this by disabling CURLOPT_FOLLOWLOCATIONand handling redirects itself, sanitizing URLs as necessary. Alternately, an app could leave CURLOPT_FOLLOWLOCATION enabled but set CURLOPT_REDIR_PROTOCOLS and install a CURLOPT_OPENSOCKETFUNCTION callback function in which addresses are sanitized before use.

  • 应用程序可以通过禁用CURLOPT_FOLLOWLOCATION并处理重定向本身,根据需要清理URL来缓解这种情况。或者,应用程序可以离开CURLOPT_FOLLOWLOCATION启用,但设置CURLOPT_REDIR_PROTOCOLS并安装一个CURLOPT_OPENSOCKETFUNCTION回调函数,其中地址在使用前已清除。

Private Resources(私人资源)

  • A user who can control the DNS server of a domain being passed in within a URL can change the address of the host to a local, private address which a server-side libcurl-using application could then use. e.g. the innocuous URL http://fuzzybunnies.example.com/ could actually resolve to the IP address of a server behind a firewall, such as 127.0.0.1 or 10.1.2.3. Apps can mitigate against this by setting a CURLOPT_OPENSOCKETFUNCTION and checking the address before a connection.

  • 可以控制在URL内传递的域的DNS服务器的用户可以将主机的地址改变为服务器侧libcurl使用应用可以使用的本地私有地址。例如无害的网址http://fuzzybunnies.example.com/实际上可以解析为防火墙后面的服务器的IP地址,例如127.0.0.1或10.1.2.3。应用程序可以通过设置CURLOPT_OPENSOCKETFUNCTION并在连接前检查地址来缓解这种情况。

  • All the malicious scenarios regarding redirected URLs apply just as well to non-redirected URLs, if the user is allowed to specify an arbitrary URL that could point to a private resource. For example, a web app providing a translation service might happily translate file://localhost/etc/passwd and display the result. Apps can mitigate against this with the CURLOPT_PROTOCOLS option as well as by similar mitigation techniques for redirections.

  • 如果允许用户指定可指向私有资源的任意URL,则关于重定向URL的所有恶意场景也适用于非重定向URL。例如,提供翻译服务的Web应用程序可能会愉快地翻译file://localhost/etc/passwd并显示结果。应用程序可以使用CURLOPT_PROTOCOLS选项以及类似的重定向缓解技术来缓解这种情况。

  • A malicious FTP server could in response to the PASV command return an IP address and port number for a server local to the app running libcurl but behind a firewall. Apps can mitigate against this by using the CURLOPT_FTP_SKIP_PASV_IP option or CURLOPT_FTPPORT.

  • 恶意FTP服务器可以响应PASV命令返回运行libcurl但在防火墙后面的应用程序本地服务器的IP地址和端口号。应用程序可以通过使用CURLOPT_FTP_SKIP_PASV_IP选项或CURLOPT_FTPPORT来缓解这种情况。

IPv6 Addresses

  • libcurl will normally handle IPv6 addresses transparently and just as easily as IPv4 addresses. That means that a sanitizing function that filters out addressses like 127.0.0.1 isn’t sufficient–the equivalent IPv6 addresses ::1, ::, 0:00::0:1, ::127.0.0.1 and ::ffff:7f00:1 supplied somehow by an attacker would all bypass a naive filter and could allow access to undesired local resources. IPv6 also has special address blocks like link-local and site-local that generally shouldn’t be accessed by a server-side libcurl-using application. A poorly-configured firewall installed in a data center, organization or server may also be configured to limit IPv4 connections but leave IPv6 connections wide open. In some cases, the CURL_IPRESOLVE_V4 option can be used to limit resolved addresses to IPv4 only and bypass these issues.

  • libcurl通常将透明地处理IPv6地址,就像IPv4地址一样容易。 这意味着过滤掉像127.0.0.1这样的地址的清理函数是不够的 - 等效的IPv6地址::1,::,0:00::0:1,::127.0.0.1和::ffff:7f00:1由攻击者提供某种方式将绕过一个朴素的过滤器,并可以允许访问不需要的本地资源。 IPv6还具有特殊的地址块,如link-local和site-local,通常不应该由服务器端libcurl使用的应用程序访问。 安装在数据中心,组织或服务器中的配置不良的防火墙也可以配置为限制IPv4连接,但使IPv6连接保持开放。 在某些情况下,CURL_IPRESOLVE_V4选项可用于将解析的地址仅限于IPv4,并绕过这些问题。

Uploads

  • When uploading, a redirect can cause a local (or remote) file to be overwritten. Apps must not allow any unsanitized URL to be passed in for uploads. Also, CURLOPT_FOLLOWLOCATION should not be used on uploads. Instead, the app should handle redirects itself, sanitizing each URL first.

  • 上传时,重定向可能会导致本地(或远程)文件被覆盖。 应用程式不得传送任何未经过验证的网址,以便上传。 此外,不应在上传时使用CURLOPT_FOLLOWLOCATION。 相反,应用程序应该处理重定向本身,首先清理每个URL。

Authentication(验证)

  • Use of CURLOPT_UNRESTRICTED_AUTH could cause authentication information to be sent to an unknown second server. Apps can mitigate against this by disabling CURLOPT_FOLLOWLOCATION and handling redirects itself, sanitizing where necessary.

  • 使用CURLOPT_UNRESTRICTED_AUTH可能会导致认证信息发送到未知的第二台服务器。 应用程序可以通过禁用CURLOPT_FOLLOWLOCATION并处理重定向本身,在必要时进行清理来缓解这种情况。

  • Use of the CURLAUTH_ANY option to CURLOPT_HTTPAUTH could result in user name and password being sent in clear text to an HTTP server. Instead, use CURLAUTH_ANYSAFE which ensures that the password is encrypted over the network, or else fail the request.

  • CURLOPT_HTTPAUTH使用CURLAUTH_ANY选项可能导致以明文形式将用户名和密码发送到HTTP服务器。 相反,请使用CURLAUTH_ANYSAFE,以确保密码在网络上加密,否则请求失败。

  • Use of the CURLUSESSL_TRY option to CURLOPT_USE_SSL could result in user name and password being sent in clear text to an FTP server. Instead, use CURLUSESSL_CONTROL to ensure that an encrypted connection is used or else fail the request.

  • CURLOPT_USE_SSL使用CURLUSESSL_TRY选项可能导致用户名和密码以明文形式发送到FTP服务器。 相反,请使用CURLUSESSL_CONTROL来确保使用加密连接,否则请求失败。

Cookies

  • If cookies are enabled and cached, then a user could craft a URL which performs some malicious action to a site whose authentication is already stored in a cookie. e.g. http://mail.example.com/delete-stuff.cgi?delete=all Apps can mitigate against this by disabling cookies or clearing them between requests.

  • 如果启用和缓存Cookie,则用户可以制作一个URL,该URL对已经存储在Cookie中的网站执行某些恶意操作。 例如 http://mail.example.com/delete-stuff.cgi?delete=all应用可以通过禁用Cookie或在请求之间清除Cookie来缓解此情况。

Dangerous URLs

  • SCP URLs can contain raw commands within the scp: URL, which is a side effect of how the SCP protocol is designed. e.g. scp://user:pass@host/a;date >/tmp/test; Apps must not allow unsanitized SCP: URLs to be passed in for downloads.

  • SCP URL可以包含scp:URL中的原始命令,这是如何设计SCP协议的副作用。 例如scp://user:pass@host/a;date >/tmp/test; 应用程式不得允许未经格式化的SCP:网址传入下载。

Denial of Service(拒绝服务)

  • A malicious server could cause libcurl to effectively hang by sending a trickle of data through, or even no data at all but just keeping the TCP connection open. This could result in a denial-of-service attack. The CURLOPT_TIMEOUT and/or CURLOPT_LOW_SPEED_LIMIT options can be used to mitigate against this.

  • 恶意服务器可能会导致libcurl通过发送涓流数据,甚至没有数据,但只是保持TCP连接打开有效地挂起。这可能导致拒绝服务攻击。可以使用CURLOPT_TIMEOUT和/或CURLOPT_LOW_SPEED_LIMIT选项来减轻这种情况。

  • A malicious server could cause libcurl to effectively hang by starting to send data, then severing the connection without cleanly closing the TCP connection. The app could install a CURLOPT_SOCKOPTFUNCTION callback function and set the TCP SO_KEEPALIVE option to mitigate against this. Setting one of the timeout options would also work against this attack.

  • 恶意服务器可能会导致libcurl有效挂起,开始发送数据,然后切断连接,而不干净地关闭TCP连接。该应用程序可以安装一个CURLOPT_SOCKOPTFUNCTION回调函数,并设置TCP SO_KEEPALIVE选项来减轻这种情况。设置其中一个超时选项也将对抗此攻击。

  • A malicious server could cause libcurl to download an infinite amount of data, potentially causing all of memory or disk to be filled. Setting the CURLOPT_MAXFILESIZE_LARGE option is not sufficient to guard against this. Instead, the app should monitor the amount of data received within the write or progress callback and abort once the limit is reached.

  • 恶意服务器可能会导致libcurl下载无限数量的数据,可能导致所有内存或磁盘被填充。设置CURLOPT_MAXFILESIZE_LARGE选项不足以防范此情况。相反,应用程序应监视在写入或进度回调中接收的数据量,并在达到限制后中止。

  • A malicious HTTP server could cause an infinite redirection loop, causing a denial-of-service. This can be mitigated by using the CURLOPT_MAXREDIRSoption.

  • 恶意HTTP服务器可能导致无限重定向循环,从而导致拒绝服务。这可以通过使用CURLOPT_MAXREDIRS选项减轻。

Arbitrary Headers(任意标题)

  • User-supplied data must be sanitized when used in options like CURLOPT_USERAGENT, CURLOPT_HTTPHEADER, CURLOPT_POSTFIELDS and others that are used to generate structured data. Characters like embedded carriage returns or ampersands could allow the user to create additional headers or fields that could cause malicious transactions.

  • 当用于诸如CURLOPT_USERAGENTCURLOPT_HTTPHEADERCURLOPT_POSTFIELDS等用于生成结构化数据的选项时,用户提供的数据必须进行清理。 诸如嵌入式回车或&符号之类的字符可以允许用户创建可能导致恶意事务的附加报头或字段。

Server-supplied Names(服务器提供的名称)

  • A server can supply data which the application may, in some cases, use as a file name. The curl command-line tool does this with –remote-header-name, using the Content-disposition: header to generate a file name. An application could also use CURLINFO_EFFECTIVE_URL to generate a file name from a server-supplied redirect URL. Special care must be taken to sanitize such names to avoid the possibility of a malicious server supplying one like “/etc/passwd”, “\autoexec.bat”, “prn:” or even “.bashrc”.

  • 服务器可以提供应用程序在某些情况下可以用作文件名的数据。 curl命令行工具使用–remote-header-name,使用Content-disposition:header生成文件名。 应用程序还可以使用CURLINFO_EFFECTIVE_URL从服务器提供的重定向网址生成文件名。 必须特别注意清理这些名称,以避免恶意服务器提供类似“/etc/passwd”,“\ autoexec.bat”,“prn:”或甚至“.bashrc”的可能性。

Server Certificates(服务器证书

  • A secure application should never use the CURLOPT_SSL_VERIFYPEER option to disable certificate validation. There are numerous attacks that are enabled by apps that fail to properly validate server TLS/SSL certificates, thus enabling a malicious server to spoof a legitimate one. HTTPS without validated certificates is potentially as insecure as a plain HTTP connection.

  • 安全应用程序不应使用CURLOPT_SSL_VERIFYPEER选项来禁用证书验证。有许多应用程序启用无法正确验证服务器TLS / SSL证书,从而使恶意服务器欺骗合法的攻击。没有经过验证的证书的HTTPS可能与纯HTTP连接不安全。

Showing What You Do(显示您做什么)

  • On a related issue, be aware that even in situations like when you have problems with libcurl and ask someone for help, everything you reveal in order to get best possible help might also impose certain security related risks. Host names, user names, paths, operating system specifics, etc. (not to mention passwords of course) may in fact be used by intruders to gain additional information of a potential target.

  • 在相关问题上,请注意,即使在您遇到libcurl有问题并要求某人帮助的情况下,为了获得最佳可能的帮助,您显示的所有内容也可能会施加某些与安全性相关的风险。事实上,入侵者可以使用主机名,用户名,路径,操作系统细节等(当然还有密码)来获取潜在目标的附加信息

  • Be sure to limit access to application logs if they could hold private or security-related data. Besides the obvious candidates like user names and passwords, things like URLs, cookies or even file names could also hold sensitive data.

  • 如果应用程序日志可以包含专用或安全相关的数据,请务必限制对应用程序日志的访问。除了明显的用户名和密码之外,像URL,cookies或甚至文件名等东西也可以保存敏感数据。

  • To avoid this problem, you must of course use your common sense. Often, you can just edit out the sensitive data or just search/replace your true information with faked data.

  • 为了避免这个问题,你必须使用你的常识。通常,您可以编辑敏感数据或只使用伪造数据搜索/替换您的真实信息。


The multi Interface(多接口)

The easy interface as described in detail in this document is a synchronous interface that transfers one file at a time and doesn’t return until it is done.

本文档中详细描述的简单界面是一个同步接口,它一次传输一个文件,并且在完成之前不会返回。

The multi interface, on the other hand, allows your program to transfer multiple files in both directions at the same time, without forcing you to use multiple threads. The name might make it seem that the multi interface is for multi-threaded programs, but the truth is almost the reverse. The multi interface allows a single-threaded application to perform the same kinds of multiple, simultaneous transfers that multi-threaded programs can perform. It allows many of the benefits of multi-threaded transfers without the complexity of managing and synchronizing many threads.

另一方面,多接口允许您的程序在两个方向同时传输多个文件,而不强制您使用多个线程。名称可能使得多接口似乎是用于多线程程序,但实际上是相反的。多接口允许单线程应用程序执行多线程程序可以执行的相同种类的多个同时传输。它允许多线程传输的许多好处,而没有管理和同步许多线程的复杂性。

To complicate matters somewhat more, there are even two versions of the multi interface. The event based one, also called multi_socket and the "normal one" designed for using with select(). See the libcurl-multi.3 man page for details on the multi_socket event based API, this description here is for the select() oriented one.

更复杂的事情,甚至有两个版本的多接口。基于事件的一个,也称为multi_socketnormal,设计用于使用select()。有关基于multi_socket事件的API的详细信息,请参阅libcurl-multi.3手册页,此处的描述是针对select()导向的。

To use this interface, you are better off if you first understand the basics of how to use the easy interface. The multi interface is simply a way to make multiple transfers at the same time by adding up multiple easy handles into a "multi stack".

要使用这个界面,如果你首先了解如何使用easy界面的基础知识,你会更好。多接口只是通过将多个简单句柄添加到“多堆栈”中来同时进行多个传送的一种方式。

You create the easy handles you want, one for each concurrent transfer, and you set all the options just like you learned above, and then you create a multi handle with curl_multi_init and add all those easy handles to that multi handle with curl_multi_add_handle.

你创建了所需的简单句柄,每个并发传递一个句柄,你设置所有的选项,就像你上面学到的,然后使用curl_multi_init创建一个多句柄,并使用curl_multi_add_handle将所有那些简单句柄添加到多句柄。

When you’ve added the handles you have for the moment (you can still add new ones at any time), you start the transfers by calling curl_multi_perform.

当你添加了你现在的句柄(你仍然可以在任何时候添加新的句柄),你可以通过调用curl_multi_perform来启动传输。

curl_multi_perform is asynchronous. It will only perform what can be done now and then return back control to your program. It is designed to never block. You need to keep calling the function until all transfers are completed.

curl_multi_perform是异步的。它将只执行现在可以做的,然后返回控制到您的程序。它被设计为从不阻止。您需要继续调用该函数,直到所有传输完成。

The best usage of this interface is when you do a select() on all possible file descriptors or sockets to know when to call libcurl again. This also makes it easy for you to wait and respond to actions on your own application’s sockets/handles. You figure out what to select() for by using curl_multi_fdset, that fills in a set of fd_set variables for you with the particular file descriptors libcurl uses for the moment.

这个接口的最佳使用是当你对所有可能的文件描述符或套接字做一个select(),以知道什么时候再次调用libcurl。这也使您很容易等待和响应自己的应用程序的套接字/句柄上的操作。你通过使用curl_multi_fdset找出了要选择的内容,它会为libcurl使用的特定文件描述符填充一组fd_set变量。

When you then call select(), it’ll return when one of the file handles signal action and you then call curl_multi_perform to allow libcurl to do what it wants to do. Take note that libcurl does also feature some time-out code so we advise you to never use very long timeouts on select() before you call curl_multi_perform again. curl_multi_timeout is provided to help you get a suitable timeout period.

然后当调用select()时,它会返回一个文件句柄信号操作,然后调用curl_multi_perform允许libcurl做它想做的事情。请注意,libcurl还具有一些超时代码,因此我们建议您在再次调用curl_multi_perform之前不要在select()上使用非常长的超时。提供curl_multi_timeout以帮助您获得适当的超时时间。

Another precaution you should use: always call curl_multi_fdset immediately before the select() call since the current set of file descriptors may change in any curl function invoke.

另一个预防措施,你应该使用:总是在调用select()之前调用curl_multi_fdset,因为当前的文件描述符集可能会在任何curl函数调用中更改。

If you want to stop the transfer of one of the easy handles in the stack, you can use curl_multi_remove_handle to remove individual easy handles. Remember that easy handles should be curl_easy_cleanuped.

如果要停止在堆栈中传递其中一个简单句柄,可以使用curl_multi_remove_handle删除单个简单句柄。记住,容易的句柄应该是curl_easy_cleanuped

When a transfer within the multi stack has finished, the counter of running transfers (as filled in by curl_multi_perform) will decrease. When the number reaches zero, all transfers are done.

当多堆栈中的传输完成时,运行传输的计数器(由curl_multi_perform填充)将减少。当数字达到零时,所有传输完成。

curl_multi_info_read can be used to get information about completed transfers. It then returns the CURLcode for each easy transfer, to allow you to figure out success on each individual transfer.

curl_multi_info_read可用于获取有关已完成传输的信息。然后它返回每个轻松传输的CURLcode,以允许您找出每个单独传输的成功。


SSL, Certificates and Other Tricks(SSL,证书和其他技巧)

[ seeding, passwords, keys, certificates, ENGINE, ca certs ]


Sharing Data Between Easy Handles(在easy handles之间共享数据)

You can share some data between easy handles when the easy interface is used, and some data is share automatically when you use the multi interface.

当使用easy interface时,您可以在简单的句柄之间共享一些数据,并且当您使用多界面时,一些数据会自动共享。

When you add easy handles to a multi handle, these easy handles will automatically share a lot of the data that otherwise would be kept on a per-easy handle basis when the easy interface is used.

当您向多个手柄添加easy handle时,这些easy handle将自动共享大量的数据,否则在使用easy interface时,这些数据会保存在每个easy handle基础上。

The DNS cache is shared between handles within a multi handle, making subsequent name resolving faster, and the connection pool that is kept to better allow persistent connections and connection re-use is also shared. If you’re using the easy interface, you can still share these between specific easy handles by using the share interface, see libcurl-share.

DNS缓存在多句柄内的句柄之间共享,使得随后的名称解析更快,并且保持更好地允许持久连接和连接重用的连接池也被共享。 如果你使用easy界面,你仍然可以通过使用共享界面在特定的简单句柄之间共享这些,请参见libcurl-share。

Some things are never shared automatically, not within multi handles, like for example cookies so the only way to share that is with the share interface.

有些东西从来不会自动共享,而不是在多个句柄内,例如Cookie,所以唯一的方法是与share interface共享。

翻译吐了

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值