1 /**
2  * @file aiot_http_api.h
3  * @brief HTTP模块头文件, 提供用HTTP协议向阿里云物联网平台上报数据的能力
4  * @date 2019-12-27
5  *
6  * @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
7  *
8  */
9 
10 #ifndef _AIOT_HTTP_API_H_
11 #define _AIOT_HTTP_API_H_
12 
13 #if defined(__cplusplus)
14 extern "C" {
15 #endif
16 
17 #include "core_stdinc.h"
18 
19 /**
20  * @brief 服务器返回的业务错误码
21  *
22  * @details
23  *
24  * 从云平台对上报消息回应的报文JSON中解析
25  */
26 typedef enum {
27     /**
28      * @brief 0, 服务端成功接收到上报的消息
29      */
30     AIOT_HTTP_RSPCODE_SUCCESS                   = 0,
31     /**
32      * @brief 10000, 服务端返回未知错误
33      */
34     AIOT_HTTP_RSPCODE_COMMON_ERROR              = 10000,
35     /**
36      * @brief 10001, 请求参数错误
37      */
38     AIOT_HTTP_RSPCODE_PARAM_ERROR               = 10001,
39     /**
40      * @brief 20001, token过期, 请调用 @ref aiot_http_auth 进行鉴权, 获取新token
41      */
42     AIOT_HTTP_RSPCODE_TOKEN_EXPIRED             = 20001,
43     /**
44      * @brief 20002, 请求的header中无token可表明设备端合法, 请调用 @ref aiot_http_auth 进行鉴权, 获取新token
45      */
46     AIOT_HTTP_RSPCODE_TOKEN_NULL                = 20002,
47     /**
48      * @brief 20003, token错误, 请调用 @ref aiot_http_auth 进行鉴权, 获取新token
49      */
50     AIOT_HTTP_RSPCODE_TOKEN_CHECK_ERROR         = 20003,
51     /**
52      * @brief 30001, 消息上报失败
53      */
54     AIOT_HTTP_RSPCODE_PUBLISH_MESSAGE_ERROR     = 30001,
55     /**
56      * @brief 40000, 设备端上报过于频繁, 触发服务端限流
57      */
58     AIOT_HTTP_RSPCODE_REQUEST_TOO_MANY          = 40000,
59 } aiot_http_response_code_t;
60 
61 /**
62  * @brief @ref aiot_http_setopt 函数的 option 参数, 对于下文每一个选项中的数据类型, 指的是 @ref aiot_mqtt_setopt 中的data参数的数据类型
63  *
64  */
65 typedef enum {
66     /**
67      * @brief HTTP 服务器的域名地址或者ip地址
68      *
69      * @details
70      *
71      * 阿里云物联网平台域名地址列表: (tcp使用80端口, tls使用443端口)
72      *
73      * |                域名地址               |  区域   | 端口号 |
74      * |---------------------------------------|---------|--------|
75      * | iot-as-http.cn-shanghai.aliyuncs.com  |  上海   |  443   |
76      *
77      * 数据类型: (char *)
78      */
79     AIOT_HTTPOPT_HOST,
80     /**
81      * @brief HTTP 服务器的端口号
82      *
83      * @details
84      *
85      * 连接阿里云物联网平台时:
86      *
87      * 1.如果使用的是tcp, 端口号设置为80
88      *
89      * 2. 如果使用的是tls, 端口号设置为443
90      *
91      * 数据类型: (uint16_t *)
92      */
93     AIOT_HTTPOPT_PORT,
94     /**
95      * @brief HTTP建联时, 网络使用的安全凭据
96      *
97      * @details
98      *
99      * 该配置项用于为底层网络配置 @ref aiot_sysdep_network_cred_t 安全凭据数据
100      *
101      * 1. 若该选项不配置, 那么MQTT将以tcp方式直接建联
102      *
103      * 2. 若 @ref aiot_sysdep_network_cred_t 中option配置为 @ref AIOT_SYSDEP_NETWORK_CRED_NONE , HTTP将以tcp方式直接建联
104      *
105      * 3. 若 @ref aiot_sysdep_network_cred_t 中option配置为 @ref AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA , HTTP将以tls方式建联
106      *
107      * 数据类型: (aiot_sysdep_network_cred_t *)
108      */
109     AIOT_HTTPOPT_NETWORK_CRED,
110     /**
111      * @brief HTTP建联时, 建立网络连接的超时时间
112      *
113      * @details
114      *
115      * 指建立socket连接的超时时间
116      *
117      * 数据类型: (uint32_t *) 默认值: (5 *1000) ms
118      *
119      */
120     AIOT_HTTPOPT_CONNECT_TIMEOUT_MS,
121     /**
122      * @brief HTTP发送数据时, 在协议栈花费的最长时间
123      *
124      * @details
125      *
126      * 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
127      */
128     AIOT_HTTPOPT_SEND_TIMEOUT_MS,
129     /**
130      * @brief HTTP接收数据时, 在协议栈花费的最长时间
131      *
132      * @details
133      *
134      * 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
135      */
136     AIOT_HTTPOPT_RECV_TIMEOUT_MS,
137     /**
138      * @brief 销毁HTTP实例时, 等待其他api执行完毕的时间
139      *
140      * @details
141      *
142      * 当调用 @ref aiot_http_deinit 销毁HTTP实例时, 若继续调用其他 aiot_http_xxx API, API会返回STATE_USER_INPUT_EXEC_DISABLED 错误
143      *
144      * 此时, 用户应该停止调用其他 aiot_http_xxx API
145      *
146      * 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
147      */
148     AIOT_HTTPOPT_DEINIT_TIMEOUT_MS,
149     /**
150      * @brief 当接收服务器返回的http报文时, 单行http header的最大长度
151      *
152      * @details
153      *
154      * 当单行http header设置过短时, @ref aiot_http_recv 会返回 @ref STATE_HTTP_HEADER_BUFFER_TOO_SHORT 状态码
155      *
156      * 数据类型: (uint32_t *) 默认值: 128
157      */
158     AIOT_HTTPOPT_HEADER_BUFFER_LEN,
159     /**
160      * @brief 当接收服务器返回的http报文时, 每次从 @ref aiot_http_recv_handler_t 回调函数中给出的body最大长度
161      *
162      * @details
163      *
164      * 数据类型: (uint32_t *) 默认值: 128
165      */
166     AIOT_HTTPOPT_BODY_BUFFER_LEN,
167     /**
168      * @brief HTTP 内部事件回调函数
169      *
170      * @details
171      *
172      * 数据类型: (aiot_http_event_handler_t)
173      */
174     AIOT_HTTPOPT_EVENT_HANDLER,
175 
176     /* 以上选项配置的数据与 CORE_HTTPOPT_XXX 共用 */
177 
178     /**
179      * @brief 用户需要SDK暂存的上下文
180      *
181      * @details
182      *
183      * 1. 当接收到HTTP数据时, 该上下文会从 @ref aiot_http_recv_handler_t 的 userdata 参数给出
184      *
185      * 2. 当HTTP内部有事件发生时, 该上下文会从 @ref aiot_http_event_handler_t 的 userdata 参数给出
186      *
187      * 数据类型: (void *)
188      */
189     AIOT_HTTPOPT_USERDATA,
190     /**
191      * @brief HTTP 数据接收回调函数
192      *
193      * @details
194      *
195      * 数据类型: (aiot_http_recv_handler_t)
196      */
197     AIOT_HTTPOPT_RECV_HANDLER,
198     /**
199      * @brief 设备的product key, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
200      *
201      * @details
202      *
203      * 数据类型: (char *)
204      */
205     AIOT_HTTPOPT_PRODUCT_KEY,
206     /**
207      * @brief 设备的device name, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
208      *
209      * @details
210      *
211      * 数据类型: (char *)
212      */
213     AIOT_HTTPOPT_DEVICE_NAME,
214     /**
215      * @brief 设备的device secret, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
216      *
217      * @details
218      *
219      * 数据类型: (char *)
220      */
221     AIOT_HTTPOPT_DEVICE_SECRET,
222     /**
223      * @brief 上报设备的扩展信息, 比如模组商ID和模组ID
224      *
225      * @details
226      *
227      * 1. 模组商ID: 格式为pid=xxx
228      *
229      * 2. 模组ID: 格式为mid=xxx
230      *
231      * 如果需要同时上报多个信息, 那么它们之间用&连接, 例如: pid=xxx&mid=xxx
232      *
233      * 数据类型: (char *)
234      */
235     AIOT_HTTPOPT_EXTEND_DEVINFO,
236     /**
237      * @brief 使用 @ref aiot_http_auth 进行认证并获取token的超时时间
238      *
239      * @details
240      *
241      * 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
242      */
243     AIOT_HTTPOPT_AUTH_TIMEOUT_MS,
244     /**
245      * @brief 是否使用http长连接
246      *
247      * @details
248      *
249      * 若该配置的值为0, 则每次使用 @ref aiot_http_auth 和 @ref aiot_http_send 时, SDK会重新与 HTTP 服务器建立简介
250      *
251      * 数据类型: (uint8_t *) 默认值: (5 * 1000) ms
252      */
253     AIOT_HTTPOPT_LONG_CONNECTION,
254 
255     AIOT_HTTPOPT_MAX
256 } aiot_http_option_t;
257 
258 /**
259  * @brief SDK收到HTTP报文, 传递给用户数据回调函数时, 对报文类型的描述
260  */
261 typedef enum {
262     /**
263      * @brief 获取到HTTP Status Code
264      */
265     AIOT_HTTPRECV_STATUS_CODE,
266     /**
267      * @brief 获取到HTTP Header, 每次返回Header中的一组键值对
268      */
269     AIOT_HTTPRECV_HEADER,
270     /**
271      * @brief 获取到HTTP Body, 返回完整的Body内容
272      */
273     AIOT_HTTPRECV_BODY
274 } aiot_http_recv_type_t;
275 
276 /**
277  * @brief SDK收到HTTP报文, 传递给用户数据回调函数时, 对报文内容的描述
278  */
279 typedef struct {
280     /**
281      * @brief HTTP 消息类型, 更多信息请参考 @ref aiot_http_recv_type_t
282      */
283     aiot_http_recv_type_t type;
284     union {
285         /**
286          * @brief HTTP 消息类型为 @ref AIOT_HTTPRECV_STATUS_CODE 时的数据
287          */
288         struct {
289             /**
290              * @brief HTTP Status Code
291              */
292             uint32_t code;
293         } status_code;
294         /**
295          * @brief HTTP 消息类型为 @ref AIOT_HTTPRECV_HEADER 时的数据
296          */
297         struct {
298             /**
299              * @brief 单行 HTTP Header 的 key
300              */
301             char *key;
302             /**
303              * @brief 单行 HTTP Header 的 value
304              */
305             char *value;
306         } header;
307         /**
308          * @brief HTTP 消息类型为 @ref AIOT_HTTPRECV_BODY 时的数据
309          */
310         struct {
311             /**
312              * @brief HTTP Body 的内容
313              */
314             uint8_t *buffer;
315             /**
316              * @brief HTTP Body 的长度
317              */
318             uint32_t len;
319         } body;
320     } data;
321 } aiot_http_recv_t;
322 
323 /**
324  * @brief HTTP 消息接收回调函数原型, 可以通过 @ref aiot_http_setopt 接口的 @ref AIOT_HTTPOPT_RECV_HANDLER 参数指定
325  *
326  * @details
327  *
328  * 当SDK收到 HTTP 服务器的应答数据时, 通过此回调函数输出
329  *
330  * @param[out] handle HTTP 句柄
331  * @param[out] packet 从 HTTP 服务器接收到的数据
332  * @param[out] userdata 用户通过 @ref AIOT_HTTPOPT_USERDATA 交由SDK暂存的上下文
333  *
334  * @return void
335  */
336 typedef void (*aiot_http_recv_handler_t)(void *handle, const aiot_http_recv_t *packet, void *userdata);
337 
338 /**
339  * @brief SDK内部发生状态变化, 通过用户事件回调函数通知用户时, 对事件类型的描述
340  */
341 typedef enum {
342     /**
343      * @brief token无效事件, 此时用户应该调用 @ref aiot_http_auth 获取新的token
344      */
345     AIOT_HTTPEVT_TOKEN_INVALID
346 } aiot_http_event_type_t;
347 
348 /**
349  * @brief SDK内部发生状态变化, 通过用户事件回调函数通知用户时, 对事件内容的描述
350  */
351 typedef struct {
352     aiot_http_event_type_t type;
353 } aiot_http_event_t;
354 
355 /**
356  * @brief HTTP 事件回调函数原型, 可以通过 @ref aiot_http_setopt 接口的 @ref AIOT_HTTPOPT_EVENT_HANDLER 参数指定
357  *
358  * @param[out] handle HTTP句柄
359  * @param[out] event 事件结构体
360  * @param[out] user_data 指向用户上下文数据的指针, 由 @ref aiot_http_setopt 的 @ref AIOT_HTTPOPT_USERDATA 选项设置
361  */
362 typedef void (* aiot_http_event_handler_t)(void *handle, const aiot_http_event_t *event, void *userdata);
363 
364 /**
365  * @brief 创建一个HTTP上云实例
366  *
367  * @return void*
368  *
369  * @retval 非NULL, HTTP 实例句柄
370  * @retval NULL,初始化 HTTP 实例失败
371  */
372 void *aiot_http_init(void);
373 
374 /**
375  * @brief 设置HTTP实例参数
376  *
377  * @param[in] handle HTTP句柄
378  * @param[in] option 配置选项, 更多信息请参考 @ref aiot_http_option_t
379  * @param[in] data 配置数据, 更多信息请参考 @ref aiot_http_option_t
380  *
381  * @return int32_t
382  *
383  * @retval STATE_SUCCESS, 成功
384  * @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
385  * @retval STATE_USER_INPUT_OUT_RANGE, 用户输入参数无效
386  * @retval STATE_SYS_DEPEND_MALLOC_FAILED, 内存分配失败
387  */
388 int32_t aiot_http_setopt(void *handle, aiot_http_option_t option, void *data);
389 
390 /**
391  * @brief 向服务器发送认证请求, 获取token
392  *
393  * @param[in] handle HTTP句柄
394  *
395  * @return int32_t
396  *
397  * @retval STATE_SUCCESS, 认证成功
398  * @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
399  * @retval STATE_USER_INPUT_MISSING_PRODUCT_KEY, 未设置必要选项ProductKey
400  * @retval STATE_USER_INPUT_MISSING_DEVICE_NAME, 未设置必要选项DeviceName
401  * @retval STATE_USER_INPUT_MISSING_DEVICE_SECRET, 未设置必要选项DeviceSecret
402  * @retval STATE_HTTP_TOKEN_LEN_ERROR, token长度错误
403  * @retval STATE_HTTP_GET_TOKEN_FAILED, 获取token失败
404  */
405 int32_t aiot_http_auth(void *handle);
406 
407 /**
408  * @brief 上报数据到物联网平台
409  *
410  * @param[in] handle HTTP句柄
411  * @param[in] topic 上报的目标topic, 在物联网平台控制的产品详情页面有设备的完整topic列表
412  * @param[in] payload 指向上报数据的指针
413  * @param[in] payload_len 上报数据的长度
414  *
415  * @return int32_t
416  *
417  * @retval STATE_SUCCESS, 上报成功
418  * @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
419  * @retval STATE_USER_INPUT_OUT_RANGE, 用户输入参数无效
420  * @retval STATE_HTTP_NOT_AUTH, 设备未认证
421  */
422 int32_t aiot_http_send(void *handle, char *topic, uint8_t *payload, uint32_t payload_len);
423 
424 /**
425  * 服务器响应数据格式为
426  * {
427  *     "code": 0,              // 业务状态码
428  *     "message": "success",   // 业务信息
429  *     "info": {
430  *         "messageId": 892687627916247040,
431  *     }
432  * }
433  */
434 
435 /**
436  * @brief 接受HTTP应答数据, 数据会从用户设置的 @ref aiot_http_event_handler_t 回调函数输出
437  *
438  * @param[in] handle HTTP句柄
439  *
440  * @return int32_t
441  *
442  * @retval >= 0, 接受到的HTTP body数据长度
443  * @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
444  * @retval STATE_USER_INPUT_NULL_POINTER, 用户输入参数为NULL
445  * @retval STATE_USER_INPUT_OUT_RANGE, buffer_len为0
446  * @retval STATE_HTTP_RSP_MSG_ERROR, 服务器应答消息错误
447  * @retval STATE_SYS_DEPEND_NWK_CLOSED, 网络连接已关闭
448  * @retval STATE_SYS_DEPEND_NWK_READ_OVERTIME, 网络接收超时
449  * @retval STATE_HTTP_RECV_LINE_TOO_LONG, HTTP单行数据过长, 内部无法解析
450  * @retval STATE_HTTP_PARSE_STATUS_LINE_FAILED, 无法解析状态码
451  * @retval STATE_HTTP_GET_CONTENT_LEN_FAILED, 获取Content-Length失败
452  */
453 int32_t aiot_http_recv(void *handle);
454 
455 /**
456  * @brief 销毁参数p_handle所指定的HTTP实例
457  *
458  * @param[in] p_handle 指向HTTP句柄的指针
459  *
460  * @return int32_t
461  *
462  * @retval STATE_SUCCESS 成功
463  * @retval STATE_USER_INPUT_NULL_POINTER 参数p_handle为NULL或者p_handle指向的句柄为NULL
464  */
465 int32_t aiot_http_deinit(void **p_handle);
466 
467 
468 #if defined(__cplusplus)
469 }
470 #endif
471 
472 #endif /* #ifndef _AIOT_HTTP_API_H_ */
473 
474