Преглед изворни кода

Merge pull request #22 from chenyong111/master

【添加】UM 文件
朱天龙 (Armink) пре 7 година
родитељ
комит
f8a7481513

+ 69 - 312
README.md

@@ -1,338 +1,95 @@
-# WebClient介绍 #
-
-本章节是webclient的使用和API说明,描述了如何使用webclient与WEB Server通信。
-
-
-### webclient设计简介 ###
-
-webclient是**HTTP**协议的客户端工具,提供与WEB Server通信的基本功能。
-一般而言,设备端运行RT-Thread开源实时系统,并使用webclient提供的API与 HTTP 服务器交互。
-
-### webclient会话结构体定义 ###
-
-webclient底层操作接口都使用了统一的webclient客户端会话:  `struct webclient_session`,它被定义成:
-
-``` C
-struct webclient_session
-{
-    /* the session socket */
-    int socket;
-    /* the response code of HTTP request */
-    int response;
-
-    /* transfer encoding */
-    char *transfer_encoding;
-    int chunk_sz;
-    int chunk_offset;
-
-    /* content_type of HTTP response */
-    char *content_type;
-    /* content_length of HTTP response */
-    int  content_length;
-
-    /* last modified timestamp of resource */
-    char *last_modified;
-
-    /* location */
-    char *location;
-
-    /* server host */
-    char *host;
-    /* HTTP request */
-    char *request;
-
-    /* private for webclient session. */
-
-    /* position of reading */
-    unsigned int position;
-
-    /* remainder of content reading */
-    size_t content_length_remainder;
-};
-```
-
-其中当服务端有回应时:
-
-* response:会存储服务端的相应代码,如果成功,服务端回复:200;详细描述请参考 HTTP 状态码表。
-* content_type:服务端提供的内容类型。
-* content_length:服务端返回的数据长度。
-
-
-
-## webclient API说明 ##
-
-
-### webclient会话接口 ###
-
-
-webclient底层接口定义了面向http这层的公共访问接口(流方式接口),可以基于这层接口进行底层的http操作。
-
-
-#### webclient_open ####
-
-
-``` C
-struct webclient_session* webclient_open(const char* URI);
-```
-
-* 功能: 打开一个webclient客户端
-* 参数1: URI 指向相应的网址,可以包括域名,特殊的端口号等。例如:
-`URI = "http://www.test.com:8080/index.html"`
-
-* 返回值: 成功返回一个webclient客户端会话;失败返回RT_NULL
-
-webclient_open用于打开一个webclient会话,默认方法为get。
-返回时,客户端就已经解析了http返回头部,里面有status_code及resp_len。
-根据这些信息可以做进一步处理,如使用webclient_read读取服务器返回的数据。
-
-
-#### webclient_open_header ####
-
-``` C
-struct webclient_session* 
-webclient_open_header(const char* URI, int method,
-				      const char* header, size_t header_sz);
-```
-
-* 功能: 在打开会话时可以加入一些自定义的HTTP请求头信息
-* 参数1:  URI,指向相应的网址,可以包括域名,特殊的端口号等。例如:
-`URI = "http://www.test.com:8080/index.html"`
-
-
-* 参数2: method,定义了打开URI的方法,当前支持GET(WEBCLIENT_GET)或POST(WEBCLIENT_POST)
-* 参数3: header信息,例如:
-
-``` C
-"Host: www.host.com\r\n"
-"User-Agent: YourAgent\r\n"
-"Content-Type: application/x-www-form-urlencoded\r\n"
+# WebClient
+
+## 1、介绍
+
+WebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。
+
+WebClient 软件包功能特点如下:
+
+- 支持 IPV4/IPV6 地址;
+- 支持 GET/POST 请求方法;
+- 支持文件的上传和下载功能;
+- 支持 HTTPS 加密传输;
+- 完善的头部数据添加和处理方式。
+
+更多软件包介绍请查看 [详细介绍](docs/introduction.md)。
+
+### 1.1 目录结构
+
+WebClient 软件包目录结构如下所示:
+
+``` 
+webclient
+├───docs 
+│   └───figures                     // 文档使用图片
+│   │   api.md                      // API 使用说明
+│   │   introduction.md             // 介绍文档
+│   │   principle.md                // 实现原理
+│   │   README.md                   // 文档结构说明  
+│   │   samples.md                  // 软件包示例
+│   │   user-guide.md               // 使用说明
+│   └───version.md                  // 版本
+├───inc                             // 头文件
+├───src                             // 源文件				
+├───samples                         // 示例代码
+|   |   webclient_get_sample        // GET 请求示例代码
+│   └───webclient_post_sample       // POST 请求示例代码
+│   LICENSE                         // 软件包许可证
+│   README.md                       // 软件包使用说明
+└───SConscript                      // RT-Thread 默认的构建脚本
 ```
 
-header信息中必须使用"CR+LF"(回车+换行)作为分隔符和结束符。
-header中每项应该符合HTTP的协议标准。
-而一些基本的信息,例如Host,HTTP/1.0等信息,如果header中不存在,webclient会自动添加。
+### 1.2 许可证
 
-* 返回值: 成功返回一个webclient客户端会话;失败返回RT_NULL
+WebClient 软件包遵循 LGPLv2.1 许可,详见 LICENSE 文件。
 
-webclient_open_header用于打开一个webclient会话,method由用户指定。
-相比webclient_open接口,webclient_open_header可以自定义请求的header。
+### 1.3 依赖
 
+- RT_Thread 3.0+
 
+- [mbedtls 软件包](https://github.com/RT-Thread-packages/mbedtls)(如果开启 HTTPS 支持)
 
-#### webclient_close ####
+## 2、获取软件包
 
+使用 WebClient 软件包需要在 RT-Thread 的包管理中选中它,具体路径如下:
 
-``` C
-int webclient_close(struct webclient_session* session);
 ```
-
-* 功能: 关闭一个webclient客户端
-* 参数1: session指向要关闭的webclient客户端会话
-* 返回值: 0
-
-webclient_close用于关闭webclient一个会话。
-
-
-
-
-### webclient数据接口 ###
-
-
-#### webclient_read ####
-
-
-``` C
-int webclient_read (struct webclient_session* session, 
-					unsigned char *buffer, size_t size);
+RT-Thread online packages
+    IoT - internet of things  --->
+		[*] WebClient: A HTTP/HTTPS Client for RT-Thread
+		[ ]   Enable support tls protocol
+		[ ]   Enable webclient GET/POST samples
+		      Version (latest)  --->
 ```
 
-* 功能: 从http连接中读取一段数据(非服务端响应的http header)
-* 参数1: session,一个webclient客户端会话
-* 参数2: buffer,保存从http连接中读取的数据的缓冲区
-* 参数3: size,每次读取的最大数据
-* 返回值: 成功返回读到的数据长度;失败返回负数
+**Enable support tls protocol** :开启 HTTPS 支持;
 
-webclient_read从webclient会话中读取数据。
+**Enable webclient GET/POST samples** :添加示例代码;
 
-#### webclient_write ####
+**Version** :配置软件包版本。
 
+配置完成后让 RT-Thread 的包管理器自动更新,或者使用 pkgs --update 命令更新包到 BSP 中。
 
-``` C
-int webclient_write(struct webclient_session* session, 
-					const unsigned char *buffer, size_t size);
-```
+## 3、使用 WebClient 软件包
+- 软件包详细介绍,请参考 [软件包介绍](docs/introduction.md)
 
-* 功能: 向http连接发送一段数据
-* 参数1: session,一个webclient客户端会话
-* 参数2: buffer,要发送的数据的缓冲区
-* 参数3: size,要发送的数据的长度
-* 返回值: 成功发送的数据长度
+- 详细的示例介绍,请参考 [示例文档](docs/samples.md) 
 
-webclient_write向webclient会话写入数据。
+- 如何从零开始使用,请参考 [用户指南](docs/user-guide.md)
 
-### webclient应用接口 ###
+- 完整的 API 文档,请参考 [API 手册](docs/api.md)
 
+- 软件包工作原理,请参考 [工作原理](docs/principle.md) 
 
-#### webclient传输数据 ####
-
-``` C
-int webclient_transfer(const char* URI, const char* header, 
-                         size_t header_sz,
-						 const char* data, size_t data_sz,
-						 char *result, size_t result_sz);
-```
-
-* 功能: 向指定的URI传递数据data(同时也设置附加的HTTP请求头部信息为header),并读取结果到result缓冲区中。函数返回读取到的数据长度。
-* 参数1: URI,指向相应的网址,可以包括域名,特殊的端口号等。
-* 参数2: header信息。
-* 参数4: header信息的长度。
-* 参数4: data,要发送的数据
-* 参数5: size,要发送的数据的长度
-* 参数6: result,用于保存从服务器接收到的数据缓冲区,当不需要保存时,可以为空。
-* 参数7: result_sz,用于保存从服务器接收到的数据缓冲区长度。
-* 返回值: 成功发送的数据长度
-
-> 注:webclient_transfer会自动创建一个session,并在传输完成后关闭session。
-
-#### webclient文件下载 ####
-
-``` C
-int webclient_get_file(const char* URI, const char* filename);
-```
-
-这个函数用于从URI下载一个文件,并保存到filename指定的路径上。保存的文件仅包括服务端提供的文件,而不包括HTTP响应的头部信息。例如下面的例子:
-
-``` C
-/*
-* 服务端的文件test.txt,放于webroot目录下(web路径的根目录下),其内容是:
-* "this is a test.\n"
-*/
-void test(void)
-{
-	/* 下载test.txt文件 */
-	webclient_get_file("http://www.test.com/test.txt", "/test.txt");
-}
-/*
-* 保存在本地根目录的test.txt文件的内容是:
-* "this is a test.\n"
-*/
-```
+- 更多**详细介绍文档**位于 [`/docs`](/docs) 文件夹下,**使用软件包进行开发前请务必查看**。
 
+## 4、注意事项
 
+ - WebClient 软件包连接 HTTPS 服务器时需要开启 WebClient 中对 TLS 功能的支持。
+ - WebClient 软件包版本更新(`V1.0.0 -> 当前最新版 V2.0.0`)后软件包中函数接口和使用流程都有所变化,若开发者代码中使用之前接口,可以适配最新版本接口,或者在版本号配置中选择 `V1.0.0` 版本。
 
 
-#### webclient文件上传 ####
-
-``` C
-int webclient_post_file(const char* URI, 
-                   const char* filename, 
-				   const char* form_data);
-```
-
-这个函数用于从filename路径的文件中读取数据,并向URI以POST方法发送这个文件的
-内容;
-例如用于上传的form是:
-
-``` C
-<form action="uploader.php" method="post" enctype="multipart/form-data">
-<label for="file">Filename:</label>
-<input type="file" name="file" id="file" />
-<br />
-<input type="submit" name="submit" value="Submit" />
-</form>
-```
-
-参数form_data可以填充服务端关心的类型信息,例如:
-
-``` C
-"name=\"file\"; filename=\"test.txt\""
-```
-
-这样,服务端可以得到filename的值是“test.txt”。
-
-## webclient测试及示例程序 ##
-
-下面的例子是一个使用webclient底层接口的例子
-
-``` C
-#include <http_client.h>
-#include <dfs_posix.h>
-#define BUF_SZ 4096
-void webclient_test(void)
-{
-	int fd = -1;
-	int offset;
-	rt_uint8_t* ptr = RT_NULL;
-	struct webclient_session* session = RT_NULL; /* webclient客户端会话 */
-
-	session = webclient_open("http://www.test.com/index.html");
-	if (session == RT_NULL)
-	{
-		rt_kprintf("open website failed.\n");
-		goto __exit;
-	}
-
-	if (session->response != 200)
-	{
-		/* 服务端给出错误的响应 */
-		rt_kprintf("wrong response: %d\n", session->response);
-		goto __exit;
-	}
-
-	if (strcmp(session->content_type, "text/html") != 0)
-	{
-		/* 不是自己关心的内容类别,退出 */
-		rt_kprintf("context_type: %d\n", session->content_type);
-		goto __exit;
-	}
-
-	fd = open("/index.html", O_WRONLY | O_CREAT, 0);
-	if (fd < 0)
-	{
-		/* 创建文件出错,返回 */
-		rt_kprintf("open file failed\n");
-		goto __exit;
-	}
-
-	/* 分配需要的缓冲 */
-	ptr = rt_malloc (BUF_SZ);
-	if (ptr == RT_NULL)
-	{
-		rt_kprintf("out of memory\n");
-		goto __exit;
-	}
-
-	if (session->content_length == 0)
-	{
-		/* 如果服务端未给出数据内容长度,读取数据直到服务端关闭连接 */
-		while (1)
-		{
-		length = webclient_read(session, ptr, BUF_SZ);
-		if (length > 0) write(fd, ptr, length);
-		else break;
-		}
-	}
-	else
-	{
-		for (offset = 0; offset < session->content_length; )
-		{
-			/* 从连接读取数据 */
-			length = webclient_read(session, ptr,
-			session->content_length - offset > BUF_SZ?
-			BUF_SZ:session->content_length - offset);
-			/* 写入到文件中 */
-			if (length > 0) write(fd, ptr, length);
-			else break;
-			/* 挪动偏移位置 */
-			offset += length;
-		}
-	}
-
-__exit: /* 退出出口 */
-	if (session != RT_NULL) webclient_close(session);
-	if (fd >= 0) close(fd);
-	if (ptr != RT_NULL) rt_free(ptr);
-	return;
-}
-```
+## 5、联系方式 & 感谢
 
+- 维护:RT-Thread 开发团队
+- 主页:https://github.com/RT-Thread-packages/webclient

+ 17 - 0
docs/README.md

@@ -0,0 +1,17 @@
+# 文档
+
+## 软件包地址
+
+- https://github.com/RT-Thread-packages/webclient
+
+## 文档列表
+
+|文件名                             |描述|
+|:-----                             |:----|
+|[version.md](version.md)           |版本信息|
+|[introduction.md](introduction.md) |详细介绍|
+|[principle.md](principle.md)       |工作原理|
+|[user-guide.md](user-guide.md)     |使用指南|
+|[api.md](api.md)                   |API 说明|
+|[samples.md](samples.md)           |示例说明|
+

+ 234 - 0
docs/api.md

@@ -0,0 +1,234 @@
+# API 说明
+
+## 创建会话
+
+`struct webclient_session *webclient_session_create(size_t header_sz);`
+
+创建客户端会话结构体。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|header_sz          | 最大支持的头部长度                   |
+| **返回**          | **描述**                            |
+|!= NULL            | webclient 会话结构体指针             |
+|= NULL             | 创建失败                            |
+
+## 关闭会话连接
+
+`int webclient_close(struct webclient_session *session);`
+
+关闭传入的客户端会话连接,并释放内存。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+| **返回**          | **描述**                            |
+|=0                 | 成功                                |
+
+
+## 发送 GET 请求
+
+`int webclient_get(struct webclient_session *session, const char *URI);`
+
+发送 HTTP GET 请求命令。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|URI                | 连接的 HTTP 服务器地址               |
+| **返回**          | **描述**                            |
+|>0                 | HTTP 响应状态码                     |
+|<0                 | 发送请求失败                        |
+
+## 发送获取部分数据的 GET 请求
+
+`int webclient_get_position(struct webclient_session *session, const char *URI, int position);`
+
+发送带有 Range 头信息的 HTTP GET 请求命令,多用于完成断点续传功能。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|URI                | 连接的 HTTP 服务器地址               |
+|position           | 数据偏移量                 |
+| **返回**          | **描述**                            |
+|>0                 | HTTP 响应状态码                     |
+|<0                 | 发送请求失败                        |
+
+## 发送 POST 请求
+
+`int webclient_post(struct webclient_session *session, const char *URI, const char *post_data);`
+
+发送 HTTP POST 请求命令,上传数据到 HTTP 服务器。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|URI                | 连接的 HTTP 服务器地址               |
+|post_data          | 需要上传的数据地址                   |
+| **返回**          | **描述**                            |
+|>0                 | HTTP 响应状态码                     |
+|<0                 | 发送请求失败                        |
+
+## 发送数据
+
+`int webclient_write(struct webclient_session *session, const unsigned char *buffer, size_t size);`
+
+发送数据到连接的服务器。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|buffer             | 发送数据的地址                       |
+|size               | 发送数据的长度                       |
+| **返回**          | **描述**                            |
+|>0                 | 成功发送数据的长度                   |
+|=0                 | 连接关闭                            |
+|<0                 | 发送数据失败                        |
+
+## 接收数据
+
+`int webclient_read(struct webclient_session *session, unsigned char *buffer, size_t size);`
+
+从连接的服务器接收数据。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|buffer             | 接收数据的存放地址                   |
+|size               | 最大接收数据的长度                   |
+| **返回**          | **描述**                            |
+|>0                 | 成功接收数据的长度                   |
+|=0                 | 连接关闭                            |
+|<0                 | 接收数据失败                        |
+
+
+## 设置接收和发送数据超时时间
+
+`int webclient_set_timeout(struct webclient_session *session, int millisecond);`
+
+设置连接的接收和发送数据超时时间。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|millisecond        | 设置的超时时间,单位毫秒             |
+| **返回**          | **描述**                           |
+|=0                 | 设置超时成功                        |
+
+## 在请求头中添加字段数据
+
+`int webclient_header_fields_add(struct webclient_session *session, const char *fmt, ...);`
+
+该函数用于创建会话之后和发送 GET 或 POST 请求之前,用于添加请求头字段数据。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|fmt                | 添加字段数据的表达式                 |
+|...                | 添加的字段数据,为可变参数        |
+| **返回**          | **描述**                           |
+| >0                | 成功添加的字段数据的长度            |
+| <=0               | 添加失败或者头部数据长度超出        |
+
+## 通过字段名获取字段值数据
+
+`const char *webclient_header_fields_get(struct webclient_session *session, const char *fields);`
+
+该函数用于发送 GET 或 POST 请求之后,可以通过传入的字段名称获取对应的字段数据。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|fields             | HTTP 字段名称                       |
+| **返回**          | **描述**                            |
+|= NULL             | 获取数据失败                        |
+|!= NULL            | 成功获取的字段数据                   |
+
+
+## 接收响应数据到指定地址
+
+`int webclient_response(struct webclient_session *session, unsigned char **response);`
+
+该函数用于发送 GET 或 POST 请求之后, 可以接收响应数据到指定地址。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+|response           | 存放接收数据的字符串地址             |
+| **返回**          | **描述**                           |
+| >0                | 成功接收数据的长度                  |
+| <=0               | 接收数据失败                        |
+
+## 发送 GET/POST 请求并接收响应数据
+
+`int webclient_request(const char *URI, const char *header, const char *post_data, unsigned char **response);`
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|URI                | 连接的 HTTP 服务器地址              |
+|header             | 需要发送的头部数据                  |
+|                   | = NULL,发送默认头数据信息,只用于发送 GET 请求       |
+|                   | != NULL,发送指定头数据信息,可用于发送 GET/POST请求  |
+|post_data          | 发送到服务器的数据                  |
+|                   | = NULL,该发送请求为 GET 请求       |
+|                   | != NULL,该发送请求为 POST 请求     |
+|response          | 存放接收数据的字符串地址             |
+| **返回**          | **描述**                           |
+| >0                | 成功接收数据的长度                  |
+| <=0               | 接收数据失败                        |
+
+
+## 获取 HTTP 响应状态码
+
+`int webclient_resp_status_get(struct webclient_session *session);`
+
+该函数用于发送 GET 或 POST 请求之后,用于获取返回的响应状态码。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+| **返回**          | **描述**                           |
+| >0                | HTTP 响应状态码                     |
+
+## 获取 Content-Length 字段数据
+
+`int webclient_content_length_get(struct webclient_session *session);`
+
+该函数用于发送 GET 或 POST 请求之后,用于获取返回的 Content-Length 字段数据。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|session            | 当前连接会话结构体指针               |
+| **返回**          | **描述**                           |
+| >0                | Content-Length 字段数据            | 
+| <0                | 获取失败                           | 
+
+## 下载文件到本地
+
+`int webclient_get_file(const char *URI, const char *filename);`
+
+从 HTTP 服务器下载文件并存放到本地。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|URI                | 连接的 HTTP 服务器地址               |
+|filename           | 存放文件位置、名称                   |
+| **返回**          | **描述**                            |
+|=0                 | 下载文件成功                        |
+|<0                 | 下载文件失败                        |
+
+## 上传文件到服务器
+
+`int webclient_post_file(const char *URI, const char *filename, const char *form_data);`
+
+从 HTTP 服务器下载文件并存放到本地。
+
+| 参数              | 描述                                |
+|:------------------|:-----------------------------------|
+|URI                | 连接的 HTTP 服务器地址               |
+|filename           | 需要上传的文件位置、名称              |
+|form_data          | 附加选项                            |
+| **返回**          | **描述**                            |
+|=0                 | 上传文件成功                        |
+|<0                 | 上传文件失败                        |

BIN
docs/figures/browse-http-Server.jpg


BIN
docs/figures/request.jpg


BIN
docs/figures/response.jpg


BIN
docs/figures/webclient_cfg.jpg


+ 139 - 0
docs/introduction.md

@@ -0,0 +1,139 @@
+# 软件包介绍
+
+WebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。
+
+## 软件包目录结构
+
+WebClient 软件包目录结构如下所示:
+
+``` 
+webclient
+├───docs 
+│   └───figures                     // 文档使用图片
+│   │   api.md                      // API 使用说明
+│   │   introduction.md             // 介绍文档
+│   │   principle.md                // 实现原理
+│   │   README.md                   // 文档结构说明  
+│   │   samples.md                  // 软件包示例
+│   │   user-guide.md               // 使用说明
+│   └───version.md                  // 版本
+├───inc                             // 头文件
+├───src                             // 源文件				
+├───samples                         // 示例代码
+|   |   webclient_get_sample        // GET 请求示例代码
+│   └───webclient_post_sample       // POST 请求示例代码
+│   LICENSE                         // 软件包许可证
+│   README.md                       // 软件包使用说明
+└───SConscript                      // RT-Thread 默认的构建脚本
+```
+
+## 软件包功能特点
+
+WebClient 软件包功能特点:
+
+- 支持 IPV4/IPV6 地址
+
+    WebClient 软件包会自动根据传入的 URI 地址的格式判断是 IPV4 地址或 IPV6 地址,并且从其中解析出连接服务器需要的信息,提高代码兼容性。
+
+- 支持 GET/POST 请求方法
+
+    HTTP 有多种请求方法(GET、POST、PUT、DELETE等),目前 WebClient 软件包支持 GET 和 POST 请求方法,这也是嵌入式设备最常用到的两个命令类型,满足设备开发需求。
+
+- 支持文件的上传和下载功能
+
+    WebClient 软件包提供文件上传和下载的接口函数,方便用户直接通过 GET/POST 请求方法上传本地文件到服务器或者下载服务器文件到本地,文件操作需要文件系统支持,使用前需开启并完成文件系统的移植。
+
+- 支持 HTTPS 加密传输
+
+    HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer)和 HTTP 协议一样是基于 TCP 实现的,实际上是在原有的 HTTP 数据外部添加一层 TLS 加密的封装,从而达到加密传输数据的目的。HTTPS 协议地址区别于 HTTP 地址,是以 `https` 开头的。WebClient 软件包中的 TLS 加密方式依赖 [mbedtls 软件包](https://github.com/RT-Thread-packages/mbedtls) 实现。
+
+- 完善的头部数据添加和处理方式
+
+    HTTP 头部信息用于确定当前请求或响应的数据和状态信息,在发送 GET/POST 请求时头部的拼接成为用户操作的一大难题,正常的做法是手动逐行输入或使用字符串拼接方式,WebClient 软件包中提供 `类正则表达式方式` 添加需要发送的请求头部信息,方便用户使用。对于请求返回的头部信息,往往用户需要获取头部字段数据,WebClient 软件包同样提供了 `通过字段名获取字段数据的方式`,方便获取需要的数据。
+
+## HTTP 协议介绍
+
+### HTTP 协议简述
+
+HTTP (Hypertext Transfer Protocol)协议, 即超文本传输协议,是互联网上应用最为广泛的一种网络协议,由于其简捷、快速的方式,适用于分布式和合作式超媒体信息系统。HTTP 协议是基于 TCP/IP 协议的网络应用层协议。默认端口为 80 端口。协议最新版本是 HTTP 2.0,目前是用最广泛的是 HTTP 1.1。
+
+HTTP 协议是一种请求/响应式的协议。一个客户端与服务器建立连接之后,发送一个请求给服务器。服务器接收到请求之后,通过接收到的信息判断响应方式,并且给予客户端相应的响应,完成整个 HTTP 数据交互流程。
+
+浏览器网页是 HTTP 的主要应用方式,但这不代表 HTTP 只能应用于网页,实际上只要通信的双方遵循 HTTP 协议数据传输合适就可以进行数据交互,比如嵌入式领域设备通过 HTTP 协议与服务器连接。
+
+### HTTP 协议特点
+
+- 无状态协议
+
+    HTTP 协议是`无状态协议`。无状态是指协议对于事件的处理没有记忆能力。这意味着如果后续处理需要前面的信息,则必须重传,这可能导致每次连接传送的数据量增大。这样的好处在于,在服务器不需要先前信息时它的应答就较快。
+
+- 灵活的数据传输
+
+    HTTP 允许传输任意类型的数据对象,传输的数据类型由 Content-Type 加以标记区分。
+
+- 简单快速
+
+    客户端向服务器发送请求时,只需要传送请求方式和路径。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
+
+- 支持 B/S 和 C/S 模式
+
+    C/S 结构,即 Client/Server (客户机/服务器)结构。B/S 结构,即 Browser/Server (浏览器/服务器)结构。
+
+### HTTP 协议请求信息 Request
+
+ 客户端发送一个HTTP请求到服务器的请求消息包括: **请求行**、**请求头部**、**空行**和**请求数据**四个部分组成。
+
+ - 请求行:用于说明请求类型,用来说明请求类型,要访问的资源以及所使用的HTTP版本;
+
+ - 请求头部:紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息;
+
+ - 空行:请求头部后面的空行是必须的,区分头部和请求数据;
+
+ - 请求数据:请求数据也叫主体,可以添加任意的其他数据。
+
+ 如下图是一个 POST 请求的信息:
+
+![HTTP 协议请求信息](figures/request.jpg)
+
+### HTTP 协议响应信息 Response
+
+一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。HTTP响应也由四个部分组成,分别是:**状态行**、**消息报头**、**空行**和**响应数据**。
+
+- 状态行:由 HTTP 协议号(HTTP 1.1),响应状态码(200),状态信息(OK)三部分组成;
+
+- 消息报头:用来说明客户端要使用的一些附加信息(日期,数据长度等);
+
+- 空行:消息报头后面的空行是必须的,用于区分消息报头和响应数据;
+
+- 响应数据:服务器返回给客户端的文本信息。
+
+如下图是一个 POST 请求的响应的信息:
+
+![HTTP 协议响应信息](figures/response.jpg)
+
+### HTTP 协议状态码
+
+HTTP 协议中通过返回的状态码信息,判断当前响应状态,WebClient 软件包中也有对状态的获取和判断方式,这里主要介绍常见的状态码的意义。
+
+状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:
+
+- 1xx:指示信息--表示请求已接收,继续处理
+
+- 2xx:成功--表示请求已被成功接收、理解、接受
+
+- 3xx:重定向--要完成请求必须进行更进一步的操作
+
+- 4xx:客户端错误--请求有语法错误或请求无法实现
+
+- 5xx:服务器端错误--服务器未能实现合法的请求
+
+常见的状态码有以下几种:
+``` 
+200 OK                        //客户端请求成功
+206 Partial Content           //服务器已经成功处理了部分 GET 请求
+400 Bad Request               //客户端请求有语法错误,不能被服务器所理解
+403 Forbidden                 //服务器收到请求,但是拒绝提供服务
+404 Not Found                 //请求资源不存在,eg:输入了错误的URL
+500 Internal Server Error     //服务器发生不可预期的错误
+503 Server Unavailable        //服务器当前不能处理客户端的请求
+```

+ 30 - 0
docs/principle.md

@@ -0,0 +1,30 @@
+# 工作原理
+
+ WebClient 软件包主要用于在嵌入式设备上实现 HTTP 协议,软件包的主要工作原理基于 HTTP 协议实现,如下图所示:
+
+![WebClient 软件包工作原理](./figures/browse-http-server.jpg)
+
+HTTP 协议定义了客户端如何从服务器请求数据,以及服务器如何把数据传送给客户端的方式。HTTP 协议采用了`请求/响应模型`。 客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
+
+在 HTTP 协议的实际使用过程中,一般遵循以下流程:
+
+1. 客户端连接到服务器
+
+    通常是通过 TCP 三次握手建立 TCP 连接,默认 HTTP 端口号为 80。
+
+2. 客户端发送 HTTP 请求(GET/POST)
+
+    通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据四部分组成
+
+3. 服务器接受请求并返回 HTTP 响应
+
+    服务器解析请求,定位请求资源。服务器将需要发送的资源写到 TCP 套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据四部分组成。
+
+4. 客户端和服务器断开连接
+
+    若客户端和服务器之间连接模式为普通模式,则服务器主动关闭 TCP 连接,客户端被动关闭连接,释放 TCP 连接。若连接模式为 keepalive 模式,则该连接保持一段时间,在该时间内可以继续接收数据。
+
+5. 客户端解析响应的数据内容
+
+    客户端获取数据后应该先解析响应状态码,判断请求是否成功,然后逐行解析响应报头,获取响应数据信息,最后读取响应数据,完成整个 HTTP 数据收发流程。
+

+ 86 - 0
docs/samples.md

@@ -0,0 +1,86 @@
+# 示例程序
+
+WebClient 软件包提供两个 HTTP Client 示例程序, 分别用于演示软件包支持的 GET 和 POST 功能,完成数据的上传与下载。
+
+**示例文件**
+
+| 示例程序路径                         | 说明  |
+| ----                                | ---- |
+| samples/webclient_get_sample.c      | GET 请求测试例程 |
+| samples/webclient_post_sample.c     | POST 请求测试例程 |
+
+## 准备工作
+
+### 获取软件包
+
+- menuconfig 配置获取软件包和示例代码
+
+    打开 RT-Thread 提供的 ENV 工具,使用 **menuconfig** 配置软件包。
+
+    启用 WebClient 软件包,并配置使能测试例程(Enable webclient GET/POST samples),如下所示:
+
+```shell
+RT-Thread online packages
+    IoT - internet of things  --->
+	    [*] WebClient: A HTTP/HTTPS Client for RT-Thread    
+	    [ ]   Enable support tls protocol       
+	    [*]   Enable webclient GET/POST samples # 开启 WebClient 测试例程
+	          Version (latest)  --->            # 开启使用最新版本软件包
+```
+
+- 使用 `pkgs --update` 命令下载软件包
+- 编译下载
+
+
+## 启动例程 
+
+本例程使用的测试网站是 RT-Thread 系统的官方网站。GET 请求示例可以从网站中获取并打印显示文件内容;POST 请求示例可以上传数据到测试网站,测试网站会响应相同的数据。
+
+> HTTP 收发数据包括头部数据和正文数据两部分,以下称头部数据为 `header 数据`,正文数据为 `body 数据`。
+
+### GET 请求示例
+
+GET 请求示例流程:
+
+- 创建 client 会话结构体
+- client 发送 GET 请求 header 数据(使用默认header 数据)
+- server 响应 header 数据和 body 数据
+- 打印 server 响应 body 数据
+- GET 请求测试完成/失败
+
+GET 请求示例使用方式有如下两种:
+
+- 在 MSH 中使用命令 `web_get_test` 执行 GET 请求示例程序,可以获取并打印显示默认网址下载的文件信息,如下图 LOG 显示:
+
+```shell
+msh />web_get_test
+webclient GET request response data :
+RT-Thread is an open source IoT operating system from China, which has strong scalability: from a tiny kernel running on a tiny core, for example ARM Cortex-M0, or Cortex-M3/4/7, to a rich feature system running on MIPS32, ARM Cortex-A8, ARM Cortex-A9 DualCore etc.
+
+msh />
+```
+
+- 在 MSH 中使用命令 `web_get_test [URI]` 格式命令执行 GET 请求示例程序,其中 URI 为用户自定义的支持 GET 请求的地址。
+
+### POST 请求示例
+
+POST 请求示例流程如下:
+
+- 创建 client 会话结构体
+- 拼接 POST 请求需要的 header 数据
+- client 发送拼接的 header 数据和 body 数据
+- server 响应 header 数据和 body 数据
+- 打印 server 响应 body 数据
+- POST 请求测试完成/失败
+
+POST 请求示例使用方式有如下两种:
+
+- 在 MSH 中使用命令 `web_post_test` 执行 POST 请求示例程序,可以获取并打印显示响应数据(默认 POST 请求的地址是类似于回显的地址,会返回上传的数据),如下图 LOG 显示:
+
+```shell
+msh />web_post_test
+webclient POST request response data :
+RT-Thread is an open source IoT operating system from China!
+msh /> 
+```
+- 在 MSH 中使用命令 `web_post_test [URI]` 格式命令执行 POST 请求示例程序,其中 URI 为用户自定义的支持 POST 请求的地址。

+ 361 - 0
docs/user-guide.md

@@ -0,0 +1,361 @@
+# 使用指南
+
+本节主要介绍 WebClient 软包的基本使用流程, 并针对使用过程中经常涉及到的结构体和重要 API 进行简要说明。
+
+## 准备工作
+
+首先需要下载 WebClient 软件包,并将软件包加入到项目中。在 BSP 目录下使用 menuconfig 命令打开 env 配置界面,在 `RT-Thread online packages → IoT - internet of things` 中选择 WebClient 软件包,操作界面如下图所示:
+
+![WebClient 软件包配置](figures/webclient_cfg.jpg)
+
+详细配置介绍如下所示:
+
+```
+RT-Thread online packages
+    IoT - internet of things  --->
+		[*] WebClient: A HTTP/HTTPS Client for RT-Thread
+		[ ]   Enable support tls protocol
+		[ ]   Enable webclient GET/POST samples
+		      Version (latest)  --->
+```
+
+**Enable support tls protocol** :开启对 HTTPS 支持;
+
+**Enable webclient GET/POST samples** :添加示例代码;
+
+**Version** :配置软件包版本号。
+
+选择合适的配置项后,使用 `pkgs --update` 命令下载软件包并更新用户配置。
+
+
+## 使用流程
+
+使用 WebClient 软件包发送 GET/POST 请求一般需要完成如下基本流程:
+
+1. **创建客户端会话结构体**
+
+```
+struct  webclient_header
+{
+    char *buffer;                       //添加或者获取的头部数据
+    size_t length;                      //存放当前头部数据长度
+
+    size_t size;                        //存放最大支持的头部数据长度
+};
+
+struct webclient_session
+{
+    struct webclient_header *header;    //保存头部信息结构体
+    int socket;                         //当前连接套接字
+    int resp_status;                    //响应状态码
+
+    char *host;                         //连接服务器地址
+    char *req_url;                      //连接的请求地址
+
+    int chunk_sz;                       //chunk 模式下一块数据大小
+    int chunk_offset;                   //chunk 模式剩余数据大小
+
+    int content_length;                 //当前接收数据长度(非 chunk 模式)
+    size_t content_remainder;           //当前剩余接收数据长度
+
+#ifdef WEBCLIENT_USING_TLS
+    MbedTLSSession *tls_session;        // HTTPS 协议相关会话结构体
+#endif
+};
+```
+
+`webclient_session` 结构体用于存放当前建立的 HTTP 连接的部分信息,可用与 HTTP 数据交互整个流程。建立 HTTP 连接前需要创建并初始化该结构体,创建的方式示例如下:
+
+```c
+struct webclient_session *session = RT_NULL;
+
+/* create webclient session and set header response size */
+session = webclient_session_create(1024);
+if (session == RT_NULL)
+{
+    ret = -RT_ENOMEM;
+    goto __exit;
+}
+```
+
+2. **拼接头部数据**
+
+ WebClient 软件包提供两种请求头部发送方式:
+
+ - 默认头部数据
+ 
+    如果要使用默认的头部信息,则不需要拼接任何头部数据,可直接调用 GET 发送命令。默认头部数据一般只用于 GET 请求。
+
+ - 自定义头部数据
+
+自定义头部数据使用 `webclient_header_fields_add` 函数添加头部信息,添加的头部信息位于客户端会话结构体中,在发送 GET/POST 请求时发送。
+
+添加示例代码如下:
+
+```c
+/* 拼接头部信息 */
+webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data));
+
+webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n");
+```
+
+3. **发送 GET/POST 请求**
+
+头部信息添加完成之后,就可以调用 `webclient_get` 函数或者 `webclient_post` 函数发送 GET/POST 请求命令了,函数中主要操作如下:
+
+- 通过传入的 URI 获取信息,建立 TCP 连接;
+
+- 发送默认或者拼接的头部信息;
+
+- 接收并解析响应数据的头部信息;
+
+- 返回错误或者响应状态码。
+
+发送 GET 请求示例代码如下:
+
+```c
+int resp_status = 0;
+
+/* send GET request by default header */
+if ((resp_status = webclient_get(session, URI)) != 200)
+{
+    LOG_E("webclient GET request failed, response(%d) error.", resp_status);
+    ret = -RT_ERROR;
+    goto __exit;
+}
+```
+
+4. **接收响应的数据**
+
+发送 GET/POST 请求之后,可以使用 `webclient_read` 函数接收响应的实际数据。因为响应的实际数据可能比较长,所以往往我们需要循环接收响应数据,指导数据接收完毕。
+
+如下所示为循环接收并打印响应数据方式:
+
+```c
+int content_pos = 0;
+/* 获取接收的响应数据长度 */
+int content_length = webclient_content_length_get(session);
+
+/* 循环接收响应数据直到数据接收完毕 */
+do
+{
+    bytes_read = webclient_read(session, buffer, 1024);
+    if (bytes_read <= 0)
+    {
+        break;
+    }
+
+    /* 打印响应数据 */
+    for (index = 0; index < bytes_read; index++)
+    {
+        rt_kprintf("%c", buffer[index]);
+    }
+
+    content_pos += bytes_read;
+} while (content_pos < content_length);
+```
+
+5. 关闭并释放客户端会话结构体
+
+请求发送并接收完成之后,需要使用 `webclient_close` 函数关闭并释放客户端会话结构体,完成整个 HTTP 数据交互流程。
+
+使用方式如下:
+
+```c
+if (session)
+{
+    webclient_close(session);
+}
+```
+
+## 使用方式
+
+WenClient 软件包对于 GET/POST 请求,分别提供了几种不同的使用方式,用于不同的情况。
+
+### GET 请求方式
+
+- 使用默认头部发送 GET 请求
+
+```c
+struct webclient_session *session = NULL;
+
+session = webclient_create(1024);
+
+if(webclient_get(session, URI) != 200)
+{
+    LOG_E("error!");
+}
+
+while(1)
+{
+    webclient_read(session, buffer, bfsz);
+    ...
+} 
+
+webclient_close(session);
+```
+
+- 使用自定义头部发送 GET 请求
+
+```c
+struct webclient_session *session = NULL;
+
+session = webclient_create(1024);
+
+webclient_header_fields_add(session, "User-Agent: RT-Thread HTTP Agent\r\n");
+
+if(webclient_get(session, URI) != 200)
+{
+    LOG_E("error!");
+}
+
+while(1)
+{
+    webclient_read(session, buffer, bfsz);
+    ...
+} 
+
+webclient_close(session);
+```
+
+- 发送获取部分数据的 GET 请求(多用于断点续传)
+
+```c
+struct webclient_session *session = NULL;
+
+session = webclient_create(1024);
+
+if(webclient_get_position(URI, 100) != 206)
+{
+    LOG_E("error!");
+}
+
+while(1)
+{
+    webclient_read(session, buffer, bfsz);
+    ...
+} 
+
+webclient_close(session);
+```
+
+- 使用 `webclient_response` 接收 GET 数据
+
+    多用于接收数据长度较小的 GET 请求。
+
+```c
+struct webclient_session *session = NULL;
+char *result;
+
+session = webclient_create(1024);
+
+if(webclient_get(session, URI) != 200)
+{
+    LOG_E("error!");
+}
+
+webclient_response(session, &result);
+
+web_free(result);
+webclient_close(session);
+```
+
+- 使用 `webclient_request` 函数发送并接收 GET 请求
+
+    多用于接收数据长度较小,且头部信息已经拼接给出的 GET 请求。
+
+```c
+char *result;    
+
+webclient_request(URI, header, NULL, &result);
+
+web_free(result);
+```
+
+### POST 请求方式
+
+- 分段数据 POST 请求
+
+    多用于上传数据量较大的 POST 请求,如:上传文件到服务器。
+
+```c
+struct webclient_session *session = NULL;
+
+session = webclient_create(1024);
+
+/*  拼接必要的头部信息 */
+webclient_header_fields_add(session, "Content-Length: %d\r\n", post_data_sz);
+webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n");
+
+/* 分段数据上传 webclient_post 第三个传输上传数据为 NULL,改为下面循环上传数据*/
+if( webclient_post(session, URI, NULL) != 200)
+{
+    LOG_E("error!");
+}
+
+while(1)
+{
+    webclient_write(session, post_data, 1024);
+    ...
+} 
+
+if( webclient_handle_response(session) != 200)
+{
+    LOG_E("error!");
+}
+
+webclient_close(session);
+```
+
+- 整段数据 POST 请求
+
+    多用于上传数据量较小的 POST 请求。
+
+```c
+char *post_data = "abcdefg";
+
+session = webclient_create(1024);
+
+/*  拼接必要的头部信息 */
+webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data));
+webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n");
+
+if(webclient_post(session, URI, post_data) != 200);
+{
+    LOG_E("error!");
+}
+webclient_close(session);
+```
+
+- 使用 `webclient_request` 函数发送 POST 请求
+
+    多用于上传文件较小且头头部信息已经拼接给出的 POST 请求。
+
+```c
+char *post_data = "abcdefg";
+char *header = "xxx";
+
+webclient_request(URI, header, post_data, NULL);
+```
+
+## 常见问题
+
+### HTTPS 地址不支持
+
+```c
+[E/WEB]not support https connect, please enable webclient https configure!
+```
+
+- 原因:使用 HTTPS 地址但是没有开启 HTTPS 支持。
+
+- 解决方法:在 WebClient 软件包 menuconfig 配置选项中开启 `Enable support tls protocol` 选项支持。
+
+### 头部数据长度超出
+
+```c
+[E/WEB]not enough header buffer size(xxx)!
+```
+
+- 原因:添加的头部数据长度超过了最大支持的头部数据长度。
+
+- 解决方法:在创建客户端会话结构体的时候,增大传入的最大支持的头部数据长度。

+ 6 - 0
docs/version.md

@@ -0,0 +1,6 @@
+# 版本和修订
+
+| Date       | Version   |  Author    | Note    |
+| --------   | :-----:   | :----      | :----   |
+| 2013-05-05 | v1.0.0    | bernard    | 初始版本 |
+| 2018-08-06 | v2.0.0    | chenyong   | 版本更新 |

+ 2 - 2
samples/webclient_get_sample.c

@@ -87,7 +87,7 @@ int webclient_get_test(int argc, char **argv)
         goto __exit;
     }
 
-    rt_kprintf("webclient GET request response data :");
+    rt_kprintf("webclient GET request response data :\n");
 
     if(webclient_header_fields_get(session, "Content-Length"))
     {
@@ -96,7 +96,7 @@ int webclient_get_test(int argc, char **argv)
 
     if (content_length < 0)
     {
-        rt_kprintf("The webclient GET request type is chunked.\n");
+        rt_kprintf("webclient GET request type is chunked.\n");
         do
         {
             bytes_read = webclient_read(session, buffer, GET_RESP_BUFSZ);

+ 1 - 1
samples/webclient_post_sample.c

@@ -94,7 +94,7 @@ int webclient_post_test(int argc, char **argv)
         goto __exit;
     }
 
-    LOG_I("webclient POST request response data :");
+    rt_kprintf("webclient POST request response data :\n");
     do
     {
         bytes_read = webclient_read(session, buffer, POST_RESP_BUFSZ);