{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/cpp-httplib-usage-guide/",
  "atlas": "https://www.pystone.net/?node=cpp-httplib-usage-guide#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/cpp-httplib-usage-guide.md",
  "context": "https://www.pystone.net/notes/cpp-httplib-usage-guide.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:cpp-httplib-usage-guide",
  "slug": "cpp-httplib-usage-guide",
  "title": "cpp-httplib使用指南",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "cpp httplib 是一个轻量级的 C++ HTTP/HTTPS 库，用于构建 HTTP 客户端和服务器。",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "e68c4d22173c13190bbba85cd1f6b50003608b87e526b89adf21579bff4576e1",
  "assets": [],
  "headings": [
    {
      "depth": 1,
      "text": "cpp-httplib使用指南",
      "anchor": "cpp-httplib使用指南",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#cpp-httplib%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97"
    },
    {
      "depth": 2,
      "text": "GET 请求格式",
      "anchor": "get-请求格式",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#get-%E8%AF%B7%E6%B1%82%E6%A0%BC%E5%BC%8F"
    },
    {
      "depth": 2,
      "text": "GET 响应格式",
      "anchor": "get-响应格式",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#get-%E5%93%8D%E5%BA%94%E6%A0%BC%E5%BC%8F"
    },
    {
      "depth": 2,
      "text": "POST 请求格式",
      "anchor": "post-请求格式",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#post-%E8%AF%B7%E6%B1%82%E6%A0%BC%E5%BC%8F"
    },
    {
      "depth": 2,
      "text": "POST 响应格式",
      "anchor": "post-响应格式",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#post-%E5%93%8D%E5%BA%94%E6%A0%BC%E5%BC%8F"
    },
    {
      "depth": 2,
      "text": "常见响应状态码",
      "anchor": "常见响应状态码",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%B8%B8%E8%A7%81%E5%93%8D%E5%BA%94%E7%8A%B6%E6%80%81%E7%A0%81"
    },
    {
      "depth": 2,
      "text": "Client 基本功能",
      "anchor": "client-基本功能",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#client-%E5%9F%BA%E6%9C%AC%E5%8A%9F%E8%83%BD"
    },
    {
      "depth": 3,
      "text": "创建 HTTP 客户端",
      "anchor": "创建-http-客户端",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%88%9B%E5%BB%BA-http-%E5%AE%A2%E6%88%B7%E7%AB%AF"
    },
    {
      "depth": 3,
      "text": "发起 GET 请求",
      "anchor": "发起-get-请求",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%8F%91%E8%B5%B7-get-%E8%AF%B7%E6%B1%82"
    },
    {
      "depth": 3,
      "text": "发起 POST 请求",
      "anchor": "发起-post-请求",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%8F%91%E8%B5%B7-post-%E8%AF%B7%E6%B1%82"
    },
    {
      "depth": 3,
      "text": "PUT请求",
      "anchor": "put请求",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#put%E8%AF%B7%E6%B1%82"
    },
    {
      "depth": 2,
      "text": "Client 配置选项",
      "anchor": "client-配置选项",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#client-%E9%85%8D%E7%BD%AE%E9%80%89%E9%A1%B9"
    },
    {
      "depth": 3,
      "text": "启用 Keep-Alive",
      "anchor": "启用-keep-alive",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%90%AF%E7%94%A8-keep-alive"
    },
    {
      "depth": 3,
      "text": "处理重定向",
      "anchor": "处理重定向",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%A4%84%E7%90%86%E9%87%8D%E5%AE%9A%E5%90%91"
    },
    {
      "depth": 3,
      "text": "自定义请求头",
      "anchor": "自定义请求头",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E8%87%AA%E5%AE%9A%E4%B9%89%E8%AF%B7%E6%B1%82%E5%A4%B4"
    },
    {
      "depth": 3,
      "text": "分块传输响应处理",
      "anchor": "分块传输响应处理",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E5%93%8D%E5%BA%94%E5%A4%84%E7%90%86"
    },
    {
      "depth": 3,
      "text": "Range 请求",
      "anchor": "range-请求",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#range-%E8%AF%B7%E6%B1%82"
    },
    {
      "depth": 2,
      "text": "Server基本功能",
      "anchor": "server基本功能",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#server%E5%9F%BA%E6%9C%AC%E5%8A%9F%E8%83%BD"
    },
    {
      "depth": 3,
      "text": "创建 HTTP 服务器",
      "anchor": "创建-http-服务器",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%88%9B%E5%BB%BA-http-%E6%9C%8D%E5%8A%A1%E5%99%A8"
    },
    {
      "depth": 3,
      "text": "文件请求处理",
      "anchor": "文件请求处理",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E6%96%87%E4%BB%B6%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86"
    },
    {
      "depth": 3,
      "text": "错误处理",
      "anchor": "错误处理",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86"
    },
    {
      "depth": 3,
      "text": "预处理路由",
      "anchor": "预处理路由",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E9%A2%84%E5%A4%84%E7%90%86%E8%B7%AF%E7%94%B1"
    },
    {
      "depth": 2,
      "text": "Server 高级功能",
      "anchor": "server-高级功能",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#server-%E9%AB%98%E7%BA%A7%E5%8A%9F%E8%83%BD"
    },
    {
      "depth": 3,
      "text": "Multipart 数据处理",
      "anchor": "multipart-数据处理",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#multipart-%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86"
    },
    {
      "depth": 3,
      "text": "Stream 数据传输",
      "anchor": "stream-数据传输",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#stream-%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93"
    },
    {
      "depth": 2,
      "text": "收发数据进阶",
      "anchor": "收发数据进阶",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E6%94%B6%E5%8F%91%E6%95%B0%E6%8D%AE%E8%BF%9B%E9%98%B6"
    },
    {
      "depth": 2,
      "text": "服务器端 (Server)",
      "anchor": "服务器端-server",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF-server"
    },
    {
      "depth": 3,
      "text": "接受数据（处理POST）",
      "anchor": "接受数据处理post",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E6%8E%A5%E5%8F%97%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86post"
    },
    {
      "depth": 4,
      "text": "1. 处理multipart/form-data",
      "anchor": "1-处理multipartform-data",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#1-%E5%A4%84%E7%90%86multipartform-data"
    },
    {
      "depth": 4,
      "text": "2. 使用 ContentReader 处理内容",
      "anchor": "2-使用-contentreader-处理内容",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#2-%E4%BD%BF%E7%94%A8-contentreader-%E5%A4%84%E7%90%86%E5%86%85%E5%AE%B9"
    },
    {
      "depth": 3,
      "text": "发送数据（处理GET）",
      "anchor": "发送数据处理get",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86get"
    },
    {
      "depth": 4,
      "text": "1. 发送数据 - 分块+流式 ContentProvider",
      "anchor": "1-发送数据---分块流式-contentprovider",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#1-%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE---%E5%88%86%E5%9D%97%E6%B5%81%E5%BC%8F-contentprovider"
    },
    {
      "depth": 4,
      "text": "2. 分块传输编码 (Chunked Transfer Encoding)",
      "anchor": "2-分块传输编码-chunked-transfer-encoding",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#2-%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81-chunked-transfer-encoding"
    },
    {
      "depth": 2,
      "text": "客户端 (Client)",
      "anchor": "客户端-client",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%AE%A2%E6%88%B7%E7%AB%AF-client"
    },
    {
      "depth": 3,
      "text": "发送数据（调用POST）",
      "anchor": "发送数据调用post",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE%E8%B0%83%E7%94%A8post"
    },
    {
      "depth": 4,
      "text": "1. 发送 multipart/form-data 数据",
      "anchor": "1-发送-multipartform-data-数据",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#1-%E5%8F%91%E9%80%81-multipartform-data-%E6%95%B0%E6%8D%AE"
    },
    {
      "depth": 4,
      "text": "2. 使用 ContentProvider 发送数据",
      "anchor": "2-使用-contentprovider-发送数据",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#2-%E4%BD%BF%E7%94%A8-contentprovider-%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE"
    },
    {
      "depth": 4,
      "text": "3. 分块传输编码 (Chunked Transfer Encoding)",
      "anchor": "3-分块传输编码-chunked-transfer-encoding",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#3-%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81-chunked-transfer-encoding"
    },
    {
      "depth": 3,
      "text": "接收数据（调用GET）",
      "anchor": "接收数据调用get",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#%E6%8E%A5%E6%94%B6%E6%95%B0%E6%8D%AE%E8%B0%83%E7%94%A8get"
    },
    {
      "depth": 4,
      "text": "1. 使用 ContentReceiver 接收数据",
      "anchor": "1-使用-contentreceiver-接收数据",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#1-%E4%BD%BF%E7%94%A8-contentreceiver-%E6%8E%A5%E6%94%B6%E6%95%B0%E6%8D%AE"
    },
    {
      "depth": 4,
      "text": "2. 进度回调",
      "anchor": "2-进度回调",
      "citation": "https://www.pystone.net/notes/cpp-httplib-usage-guide/#2-%E8%BF%9B%E5%BA%A6%E5%9B%9E%E8%B0%83"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:programming-languages-and-runtimes",
      "title": "编程语言与运行时",
      "url": "https://www.pystone.net/notes/programming-languages-and-runtimes/",
      "atlas": "https://www.pystone.net/?node=programming-languages-and-runtimes#knowledge-atlas",
      "label": "编程语言与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "本层中的“cpp-httplib使用指南”导航项",
      "citation": "https://www.pystone.net/notes/programming-languages-and-runtimes/#%E6%9C%AC%E5%B1%82"
    }
  ],
  "contentMarkdown": "`cpp-httplib` 是一个轻量级的 C++ HTTP/HTTPS 库，用于构建 HTTP 客户端和服务器。\n\n# cpp-httplib使用指南\n## **GET 请求格式**\nGET 请求通常用于请求数据，它的特点是将请求参数附加在 URL 中，并且请求体通常为空。\n\n**请求行**：\n```text\nGET /path/resource?key1=value1&key2=value2 HTTP/1.1\n```\n- **GET**：请求方法，表示请求资源。\n- **/path/resource?key1=value1&key2=value2**：请求的路径及查询参数，查询参数通过 `?` 和 `&` 分隔。\n- **HTTP/1.1**：HTTP 协议版本。\n\n**请求头**：\n```yaml\nHost: www.example.com\nUser-Agent: Mozilla/5.0\nAccept: text/html\n```\n\n- **Host**：目标服务器的域名。\n- **User-Agent**：客户端（如浏览器）的信息。\n- **Accept**：客户端可以处理的内容类型，如 `text/html`。\n\n- **空行**：\n    - 请求头后跟一个空行，表示请求头结束。\n\n- **请求体**：\n    - **GET 请求没有请求体**，所有数据通过 URL 查询字符串传递。\n\n## GET 响应格式\n**状态行**：\n```text\nHTTP/1.1 200 OK\n```\n- **HTTP/1.1**：HTTP 协议版本。\n- **200**：状态码，表示请求成功。\n- **OK**：状态描述。\n\n**响应头**：\n```yaml\nContent-Type: text/html\nContent-Length: 138\n```\n- **Content-Type**：响应体的 MIME 类型，如 `text/html` 表示 HTML 页面。\n- **Content-Length**：响应体的长度，以字节为单位。\n\n**空行**：\n- 响应头后跟一个空行，表示响应头结束。\n\n**响应体**：\n```markdown\n<h1>Hello, World!</h1>\n```\n- **响应体**包含实际的内容，如 HTML 页面、JSON 数据等。\n\n## POST 请求格式\nPOST 请求用于向服务器提交数据，如表单数据或文件上传。与 GET 不同，POST 请求的数据在请求体中传输，而不是通过 URL。\n\n**请求行**：\n```text\nPOST /path/resource HTTP/1.1\n```\n- **POST**：请求方法，表示提交数据。\n- **/path/resource**：请求的路径，不包括查询参数。\n- **HTTP/1.1**：HTTP 协议版本。\n\n**请求头**：\n```text\nHost: www.example.com\nUser-Agent: Mozilla/5.0\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 27\n```\n\n- **Content-Type**：请求体的数据类型，如 `application/x-www-form-urlencoded` 表示表单数据，`multipart/form-data` 表示文件上传。\n- **Content-Length**：请求体的长度。\n\n**空行**：\n- 请求头后跟一个空行，表示请求头结束。\n\n**请求体**：\n```text\nkey1=value1&key2=value2\n```\n- **请求体**包含实际发送的数据，如表单字段或文件内容。\n\n## **POST 响应格式**\n与 GET 响应类似，POST 响应包含状态行、响应头、空行和响应体。\n**状态行**：\n```text\nHTTP/1.1 200 OK\n```\n\n**响应头**：\n```text\nContent-Type: application/json\nContent-Length: 85\n```\n\n- **空行**：\n    - 响应头后跟一个空行，表示响应头结束。\n- **响应体**：\n```json\n{\n  \"status\": \"success\",\n  \"message\": \"Data received successfully\",\n  \"data\": {\n    \"key1\": \"value1\",\n    \"key2\": \"value2\"\n  }\n}\n```\n\n- **响应体**包含服务器返回的数据，通常是 JSON、HTML、XML 等格式。\n\n## 常见响应状态码\n\n1xx: 信息响应\n- **100 Continue**：表示到目前为止一切正常，客户端应继续请求。\n- **101 Switching Protocols**：服务器已接受客户端的请求，并正在更改协议。\n2xx: 成功\n- **200 OK**：请求已成功，服务器已返回所请求的数据。\n- **201 Created**：请求已成功并且服务器创建了一个新的资源。\n- **202 Accepted**：服务器已接受请求，但尚未处理完成。\n- **204 No Content**：服务器成功处理了请求，但没有返回任何内容。\n3xx: 重定向\n- **301 Moved Permanently**：请求的资源已被永久移动到新的URL。\n- **302 Found**：请求的资源临时从不同的URL响应请求。\n- **304 Not Modified**：资源未被修改，客户端可以使用缓存的版本。\n4xx: 客户端错误\n- **400 Bad Request**：服务器无法理解请求，因为请求格式错误。\n- **401 Unauthorized**：请求需要用户认证。\n- **403 Forbidden**：服务器理解请求但拒绝执行。\n- **404 Not Found**：服务器找不到请求的资源。\n- **405 Method Not Allowed**：请求方法不被允许。\n5xx: 服务器错误\n- **500 Internal Server Error**：服务器在处理请求时遇到错误。\n- **501 Not Implemented**：服务器不支持请求的方法。\n- **502 Bad Gateway**：服务器作为网关或代理，从上游服务器收到无效响应。\n- **503 Service Unavailable**：服务器暂时无法处理请求，通常是由于超载或维护。\n- **504 Gateway Timeout**：服务器作为网关或代理，没有及时从上游服务器接收到请求。\n\n\n## Client 基本功能\n\n### 创建 HTTP 客户端\n\n```cpp\nhttplib::Client cli(\"httpbin.org\");\n```\n\n- 创建一个连接到 `httpbin.org` 的 HTTP 客户端。\n\n### 发起 GET 请求\n\n```cpp\nauto res = cli.Get(\"/hello\");\n```\n\n- 发送一个 `GET` 请求到 `httpbin.org/hello`，并返回响应对象 `res`。\n\n`cli.Get(const char* path, const Headers& headers, ResponseHandler response_handler, ContentReceiver content_receiver)`\n最完整的形式，既可以指定请求头，还可以添加两个回调函数：\n\n```cpp\nauto res = cli.Get(\"/stream\", {},\n  [&](const httplib::Response &response) {\n    // Handle response headers\n    return true;\n  },\n  [&](const char *data, size_t data_length) {\n    // Handle data chunks\n    return true;\n  });\n\n```\n\n- **`response_handler`**：在接收到服务器的响应头时调用。\n- **`content_receiver`**：在接收到响应体的每个数据块时调用。\n\n\n> 知识补充 --- lambda 表达式的捕获列表:\n\t在 C++ 中，lambda 表达式的方括号部分（也称为捕获列表）用来指定 lambda 表达式可以捕获并使用的外部变量。方括号中的内容决定了 lambda 表达式如何访问它所在的作用域中的变量。\n\n 捕获列表 `[&, data]` 的含义：\n1. **`&`（按引用捕获）**：\n    - 捕获列表中的 `&` 表示按引用捕获外部作用域中的所有变量。这意味着 lambda 表达式可以通过引用访问其作用域中的所有变量，因此可以修改这些变量的值。\n    - 例如，如果外部作用域中有一个变量 `counter`，那么在 lambda 中对 `counter` 的修改会直接影响到外部的 `counter` 变量。\n2. **`data`（按值捕获）**：\n    - 捕获列表中的 `data` 指定了一个特定的变量 `data`，并且是按值捕获。这意味着 lambda 表达式内部有一个 `data` 的副本，lambda 内对 `data` 的修改不会影响到外部的 `data` 变量。\n    - 在这段代码中，`data` 是一个指针，按值捕获意味着 lambda 捕获了 `data` 指针的副本，而不是指针所指向的对象的副本。换句话说，lambda 内部可以使用 `data` 指针访问实际的字符串数据，但不能改变 `data` 指针本身指向的地址。\n\n### 发起 POST 请求\n\n```cpp\nauto res = cli.Post(\"/post\", \"body content\", \"text/plain\");\n```\n\n- 向指定路径发送 `POST` 请求，并附带请求体和 `Content-Type`。\n\n\n### PUT请求\n`res = cli.Put(\"/resource/foo\", \"text\", \"text/plain\");` 这行代码使用 `cpp-httplib` 库中的 `httplib::Client` 类向服务器发送了一个 HTTP `PUT` 请求。\n\nHTTP 的 `PUT` 方法通常用于更新服务器上的某个资源。它与 `POST` 不同，`PUT` 是幂等的，这意味着同样的 `PUT` 请求被多次发送，其结果应是相同的。如果资源 `/resource/foo` 已经存在，`PUT` 请求会更新它；如果资源不存在，则 `PUT` 请求通常会创建它。\n\n当服务器收到这个 `PUT` 请求时，它会：\n1. 检查 URL `/resource/foo` 对应的资源是否存在。\n2. 使用请求体中的 `\"text\"` 作为新的资源内容，替换或创建资源。\n3. 返回一个响应，通常带有状态码（如 200 OK 表示成功，或 201 Created 表示资源已创建）。\n\n使用场景:\n- **更新资源**：客户端向服务器发送更新请求，例如更新用户信息、配置文件或其他可修改的资源。\n- **创建资源**：如果服务器支持在目标资源不存在时创建新资源，`PUT` 也可以用于创建新资源。\n\n\n## Client 配置选项\n\n### 启用 Keep-Alive\n\n```cpp\ncli.set_keep_alive(true);\n```\n\n- 启用 HTTP Keep-Alive，保持与服务器的连接，避免每次请求都重新建立连接。\n\n### 处理重定向\n\n```cpp\ncli.set_follow_location(true);\nauto res = cli.Get(\"/\");\n```\n\n- 自动跟随服务器返回的重定向（例如 `301`、`302`）。\n\n### 自定义请求头\n```cpp\nauto res = cli.Get(\"/path\", {{\"Authorization\", \"Bearer token\"}});\n```\n\n- 在请求中添加自定义头信息。\n\n### 分块传输响应处理\n\n```cpp\nauto res = cli.Get(\"/stream\", Headers(),\n  [&](const Response &response) { return true; },\n  [&](const char *data, size_t data_length) { return true; });\n\n```\n\n- 使用回调函数逐块处理服务器返回的数据，适合处理大数据或流媒体内容。\n### Range 请求\n```cpp\nauto res = cli.Get(\"/range/32\", {httplib::make_range_header({{1, 10}})});\n```\n\n- 使用 `Range` 头部，只请求服务器上资源的特定字节范围。\n\n- `cli.Get(\"/range/32\", {...});` 发起了一个 `GET` 请求，请求路径是 `/range/32`，它会返回一个长度为 32 字节的资源。\n- 第二个参数 `{httplib::make_range_header({{1, 10}})}` 是一个 `Range` 头的设置，表示请求服务器返回文件从字节 1 到字节 10 的内容。\n\n\n## Server基本功能\n\n### 创建 HTTP 服务器\n\n```cpp\nhttplib::Server svr;\n\nsvr.Get(\"/hi\", [](const httplib::Request &req, httplib::Response &res) {\n    res.set_content(\"Hello World!\", \"text/plain\");\n});\nsvr.listen(\"localhost\", 8080);\n\n```\n\n- 创建一个 HTTP 服务器并监听 `localhost:8080` 端口，响应 `/hi` 路径的 GET 请求。\n\n### 文件请求处理\n```cpp\nsvr.set_file_request_handler([](const httplib::Request &req, httplib::Response &res) {\n  // Custom logic before serving a file\n});\n\n```\n- 处理静态文件请求前自定义处理逻辑。\n\n\n### 错误处理\n```cpp\nsvr.set_error_handler([](const httplib::Request &req, httplib::Response &res) {\n  res.set_content(\"Error Status: %d\", \"text/html\");\n});\n\n```\n\n- 当服务器返回非 2xx 状态码时，使用自定义错误处理函数生成错误响应。\n\n### 预处理路由\n\n在处理客户端请求之前，这个处理器会先执行，允许你在实际路由处理器之前对请求进行拦截和处理。\n\n```cpp\nsvr.set_pre_routing_handler([](const httplib::Request &req, httplib::Response &res) {\n  if (req.path == \"/hello\") {\n    res.set_content(\"world\", \"text/html\");\n    return httplib::Server::HandlerResponse::Handled;\n  }\n  return httplib::Server::HandlerResponse::Unhandled;\n});\n\n```\n\n\n## Server 高级功能\n\n### Multipart 数据处理\n\n```cpp\nsvr.Post(\"/multipart\", [&](const httplib::Request &req, httplib::Response &res) {\n  if (req.is_multipart_form_data()) {\n    req.get_file_value(\"name1\");\n    // Handle file content\n  }\n});\n```\n\n- 处理 `multipart/form-data` 类型的 POST 请求，适用于文件上传等场景。\n\n\n### Stream 数据传输\n```cpp\nconst size_t DATA_CHUNK_SIZE = 4;\n\nsvr.Get(\"/stream\", [&](const httplib::Request &req, httplib::Response &res) {\n  auto data = new std::string(\"abcdefg\");\n\n  res.set_content_provider(\n    data->size(), \"text/plain\",\n    [&, data](size_t offset, size_t length, httplib::DataSink &sink) {\n      sink.write(&data->at(offset), std::min(length, DATA_CHUNK_SIZE));\n      return true;\n    },\n    [data](bool success) { delete data; });\n});\n\n```\n\n- 使用 `set_content_provider` 实现数据流式传输，适合大文件或实时数据传输。\n\n\n## 收发数据进阶\n\n## 服务器端 (Server)\n### 接受数据（处理POST）\n#### 1. 处理`multipart/form-data`\n\n- **用途**：处理带有文件上传的表单数据。\n\n```cpp\nsvr.Post(\"/multipart\", [&](const auto& req, auto& res) {\n  auto size = req.files.size();  // 获取上传文件的数量\n  auto ret = req.has_file(\"name1\");  // 检查是否有名为 \"name1\" 的文件\n  const auto& file = req.get_file_value(\"name1\");  // 获取文件内容和元数据\n});\n```\n\n\n#### 2. 使用 ContentReader 处理内容\n\n- **用途**：接收和处理大数据流或文件上传，适用于 `multipart/form-data` 以及普通的 `POST` 请求。\n\n```cpp\nsvr.Post(\"/content_receiver\",\n  [&](const Request &req, Response &res, const ContentReader &content_reader) {\n    if (req.is_multipart_form_data()) {\n      MultipartFormDataItems files;\n      content_reader(\n        [&](const MultipartFormData &file) {\n          files.push_back(file);\n          return true;\n        },\n        [&](const char *data, size_t data_length) {\n          files.back().content.append(data, data_length);\n          return true;\n        });\n    } else {\n      std::string body;\n      content_reader([&](const char *data, size_t data_length) {\n        body.append(data, data_length);\n        return true;\n      });\n    }\n  });\n```\n\n**`content_reader(...)`**：`content_reader` 是一个回调函数，用于读取和处理 `multipart/form-data` 请求的每个部分。它接收两个回调：\n- 第一个回调用于处理每个文件部分的元数据（如文件名、内容类型），并将其添加到 `files` 容器中。\n- 第二个回调用于处理文件的实际内容，将读取到的数据追加到相应文件的 `content` 字段中。\n- `content_reader` 会一直阻塞，直到所有表单字段和文件内容都被读取完成。\n### 发送数据（处理GET）\n#### 1. 发送数据 - 分块+流式 ContentProvider\n\n- **用途**：服务器分块或流式发送响应数据，适合大数据或长时间运行的请求。\n\n```cpp\n#include \"httplib.h\"\n#include <iostream>\n\nconst size_t DATA_CHUNK_SIZE = 4; // 每次发送的数据块大小\n\nint main() {\n    httplib::Server svr;\n\n    svr.Get(\"/stream\", [&](const httplib::Request &req, httplib::Response &res) {\n        auto data = std::make_shared<std::string>(\"abcdefg\");  // 需要发送的完整数据\n\n        res.set_content_provider(\n            data->size(), // 设置内容长度\n            \"text/plain\", // 设置内容类型\n            [data](size_t offset, size_t length, httplib::DataSink &sink) {\n                // 计算本次要发送的数据长度\n                size_t chunk_size = std::min(length, DATA_CHUNK_SIZE);\n\n                // 发送数据块\n                sink.write(data->data() + offset, chunk_size);\n\n                // 返回 true 继续发送下一块，直到所有数据发送完成\n                return true;\n            },\n            [data](bool success) {\n                if (success) {\n                    std::cout << \"Data sent successfully!\" << std::endl;\n                } else {\n                    std::cerr << \"Failed to send data!\" << std::endl;\n                }\n            }\n        );\n    });\n\n    svr.listen(\"localhost\", 8080);\n    return 0;\n}\n\n```\n\n`cpp-httplib` 内部自动管理 `offset` 和 `length`。当你使用 `content_provider` 时，`cpp-httplib` 会根据每次调用 `sink.write` 时的数据长度自动更新 `offset`。\n\n也可以不通过参数设定数据的总长度：\n```cpp\nsvr.Get(\"/stream\", [&](const Request &req, Response &res) {\n  res.set_content_provider(\n    \"text/plain\", // Content type\n    [&](size_t offset, DataSink &sink) {\n      if (/* there is still data */) {\n        std::vector<char> data;\n        // prepare data...\n        sink.write(data.data(), data.size());\n      } else {\n        sink.done(); // No more data\n      }\n      return true;\n    });\n});\n\n```\n\n#### 2. 分块传输编码 (Chunked Transfer Encoding)\n\n- **用途**：处理数据流，使用 HTTP 的 `Chunked Transfer Encoding`，适合动态生成内容或长时间流式传输的情况。\n\n```cpp\nsvr.Get(\"/chunked\", [&](const Request& req, Response& res) {\n  res.set_chunked_content_provider(\n    \"text/plain\",\n    [](size_t offset, DataSink &sink) {\n      sink.write(\"123\", 3);\n      sink.write(\"345\", 3);\n      sink.write(\"789\", 3);\n      sink.done(); // No more data\n      return true;\n    }\n  );\n});\n```\n\n## 客户端 (Client)\n### 发送数据（调用POST）\n\n#### 1. 发送 `multipart/form-data` 数据\n- **用途**：客户端发送带有文件或字段的 `multipart/form-data` 请求。\n\n```cpp\nhttplib::MultipartFormDataItems items = {\n  { \"text1\", \"text default\", \"\", \"\" },\n  { \"text2\", \"aωb\", \"\", \"\" },\n  { \"file1\", \"h\\ne\\n\\nl\\nl\\no\\n\", \"hello.txt\", \"text/plain\" },\n  { \"file2\", \"{\\n  \\\"world\\\", true\\n}\\n\", \"world.json\", \"application/json\" },\n  { \"file3\", \"\", \"\", \"application/octet-stream\" },\n};\n\nauto res = cli.Post(\"/multipart\", items);\n```\n\n\n**`multipart/form-data` 请求的格式**\n\n**请求行与请求头**\n```css\nPOST /path HTTP/1.1\nHost: www.example.com\nUser-Agent: Mozilla/5.0\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\n\n```\n- **Content-Type**: `multipart/form-data` 指定了请求体包含多个部分，并且这些部分之间用 `boundary` 分隔。\n- **boundary**: 是一个字符串，用于分隔请求体中的不同部分。它通常是由 `----` 和一个随机生成的字符串组成，如 `----WebKitFormBoundary7MA4YWxkTrZu0gW`。\n\n**请求体**\n请求体包含实际的数据，分为多个部分，每个部分由 `boundary` 分隔，并且每个部分的格式如下：\n**普通字段**\n```css\n--boundary\nContent-Disposition: form-data; name=\"field_name\"\n\nfield_value\n```\n**文件字段**\n```css\n--boundary\nContent-Disposition: form-data; name=\"file_field_name\"; filename=\"filename.jpg\"\nContent-Type: image/jpeg\n\n(二进制文件数据)\n```\n\n**结束标记**\n```css\n--boundary--\n```\n\n假设你正在上传一个包含文本字段和文件的表单，表单字段包括 `text_field` 和 `file_field`。HTTP 请求可能如下所示：\n```css\nPOST /upload HTTP/1.1\nHost: www.example.com\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\n\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"text_field\"\n\nsample text\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"file_field\"; filename=\"example.jpg\"\nContent-Type: image/jpeg\n\n(binary data of example.jpg)\n----WebKitFormBoundary7MA4YWxkTrZu0gW--\n```\n\n#### 2. 使用 ContentProvider 发送数据\n- **用途**：客户端流式发送大数据，通过 `ContentProvider` 动态提供数据块。\n\n```cpp\nstd::string body = ...;\n\nauto res = cli.Post(\n  \"/stream\", body.size(),\n  [](size_t offset, size_t length, DataSink &sink) {\n    sink.write(body.data() + offset, length);\n    return true;\n  },\n  \"text/plain\");\n```\n\n#### 3. 分块传输编码 (Chunked Transfer Encoding)\n- **用途**：客户端使用 `Chunked Transfer Encoding` 发送数据流。\n\n```cpp\nauto res = cli.Post(\n  \"/stream\",\n  [](size_t offset, DataSink &sink) {\n    sink.os << \"chunked data 1\";\n    sink.os << \"chunked data 2\";\n    sink.os << \"chunked data 3\";\n    sink.done();\n    return true;\n  },\n  \"text/plain\");\n\n```\n\n### 接收数据（调用GET）\n\n#### 1. 使用 ContentReceiver 接收数据\n\n```cpp\nstd::string body;\n\nauto res = cli.Get(\"/large-data\",\n  [&](const char *data, size_t data_length) {\n    body.append(data, data_length);\n    return true;\n  });\n\n```\n\n\n#### 2. 进度回调\n\n```cpp\nhttplib::Client cli(\"http://example.com\");\n\nstd::string body;\n\nauto res = cli.Get(\n    \"/large-data\",\n    [&](const char *data, size_t data_length) {\n        // 数据接收回调\n        body.append(data, data_length);\n        return true; // 返回 true 表示继续接收数据\n    },\n    [&](uint64_t len, uint64_t total) {\n        // 进度回调\n        printf(\"%llu / %llu bytes => %d%% complete\\n\",\n               len, total,\n               (int)(len * 100 / total));\n        return true; // 返回 true 表示继续下载\n    }\n);\n\n```\n"
}
