acl  3.5.3.0
http_aclient.hpp
浏览该文件的文档.
1 #pragma once
2 #include "../acl_cpp_define.hpp"
3 #include "../stream/aio_socket_stream.hpp"
4 #if !defined(_WIN32) && !defined(_WIN64)
5 #include <netinet/in.h> // just for "struct sockaddr_storage"
6 #endif
7 
8 struct HTTP_HDR;
9 struct HTTP_HDR_RES;
10 struct HTTP_RES;
11 struct HTTP_HDR_REQ;
12 struct HTTP_REQ;
13 
14 struct ACL_ASTREAM_CTX;
15 
16 namespace acl {
17 
18 class string;
19 class aio_handle;
20 class aio_socket_stream;
21 class socket_stream;
22 class zlib_stream;
23 class websocket;
24 class sslbase_conf;
25 class sslbase_io;
26 class http_header;
27 
28 /**
29  * HTTP 客户端异步通信类,不仅支持标准 HTTP 通信协议,而且支持 Websocket 通信,
30  * 对于 HTTP 协议及 Websocket 通信均支持 SSL 加密传输;
31  * 另外,对于 HTTP 协议,根据用户设置,可以自动解压 GZIP 响应数据,这样在回调
32  * 方法 on_http_res_body() 中收到的便是解压后的明文数据。
33  */
35 {
36 public:
37  /**
38  * 构造函数
39  * @param handle {aio_handle&} 异步通信事件引擎句柄
40  * @param ssl_conf {sslbase_conf*} 非 NULL 时自动采用 SSL 通信方式
41  */
42  http_aclient(aio_handle& handle, sslbase_conf* ssl_conf = NULL);
43  virtual ~http_aclient(void);
44 
45  /**
46  * 当对象销毁时的回调方法,子类必须实现
47  */
48  virtual void destroy(void) = 0;
49 
50  /**
51  * 获得 HTTP 请求头,以便于应用添加 HTTP 请求头中的字段内容
52  * @return {http_header&}
53  */
54  http_header& request_header(void);
55 
56  /**
57  * 针对 HTTP 协议的响应数据是否自动进行解压
58  * @param on {bool}
59  * @return {http_aclient&}
60  */
61  http_aclient& unzip_body(bool on);
62 
63  /**
64  * 是否针对 GZIP 压缩数据自动进行解压
65  * @return {bool}
66  */
67  bool is_unzip_body(void) const
68  {
69  return unzip_;
70  }
71 
72  /**
73  * 除可以在构造函数中设置 SSL conf 外,还可以通过此方法设置,如果在
74  * 构造函数中设置的 ssl_conf 为 NULL,则内部自动将 ssl_enable_ 置为
75  * false,通过本方法设置了 ssl_conf 后还需调用下面的 enable_ssl()
76  * 方法以启用 ssl 功能
77  * @param ssl_conf {sslbase_conf*} 为 NULL 时将取消 SSL功能
78  * @return {http_aclient&}
79  */
80  http_aclient& set_ssl_conf(sslbase_conf* ssl_conf);
81 
82  /**
83  * 获得设置的 SSL 配置
84  * @return {sslbase_conf*} 为 NULL 表示未设置
85  */
87  {
88  return ssl_conf_;
89  }
90 
91  /**
92  * 当构造函数中 ssl_conf 参数非空时,可以调用此方法来设置是否启用 SSL
93  * 功能,如果 ssl_conf 非空,则内部 ssl_enable_ 缺省值为 true,可以通
94  * 过本方法关闭或开启 ssl 功能
95  * @param yes {bool}
96  * @return {http_aclient&}
97  */
98  http_aclient& enable_ssl(bool yes);
99 
100  /**
101  * 判断内部是否启用了 ssl 功能
102  * @return {bool}
103  */
104  bool is_enable_ssl(void) const
105  {
106  return ssl_enable_ && ssl_conf_;
107  }
108 
109  /**
110  * 开始异步连接远程 WEB 服务器
111  * @param addr {const char*} 远程 WEB 服务器地址,格式为:
112  * domain:port 或 ip:port, 当地址为域名时,内部自动进行异步域名解析
113  * 过程,但要求在程序开始时必须通过 aio_handle::set_dns() 设置过域名
114  * 服务器地址,如果地址为 IP 则不需要先设置域名服务器地址
115  * @param conn_timeout {int} 连接超时时间(秒)
116  * @param rw_timeout {int} 网络 IO 读写超时时间(秒)
117  * @return {bool} 返回 false 表示连接失败,返回 true 表示进入异步连接中
118  */
119  bool open(const char* addr, int conn_timeout, int rw_timeout);
120 
121  /**
122  * 异步关闭连接
123  */
124  void close(void);
125 
126  /**
127  * 获得本次连接(无论成功或失败)所使用的 DNS 服务地址
128  * @param out {string&} 存储结果
129  * @return {bool} 返回 false 表示没有可用的 DNS 地址
130  */
131  bool get_ns_addr(string& out) const;
132 
133  /**
134  * 当连接成功、连接失败或连接超时时可调用此方法获得当前所连接用的应用服务器地址
135  * @param out {string&} 存储结果
136  * @return {bool} 返回 false 表示还没有设置所连接服务器的地址
137  */
138  bool get_server_addr(string& out) const;
139 
140  /**
141  * 连接成功后可调用本方法获得异步连接对象
142  * @return {aio_socket_stream*}
143  */
145  {
146  return conn_;
147  }
148 
149 protected:
150  /**
151  * 当连接成功后的回调方法,子类必须实现,子类应在该方法里构造 HTTP 请求
152  * 并调用 send_request 方法向 WEB 服务器发送 HTTP 请求
153  * @return {bool} 该方法如果返回 false 则内部会自动关闭连接
154  */
155  virtual bool on_connect(void) = 0;
156 
157  /**
158  * 当域名解析失败时的回调方法,在调用本方法后,内部自动调用 this->destroy() 方法
159  */
160  virtual void on_ns_failed(void) {}
161 
162  /**
163  * 当连接超时后的回调方法,在调用本方法后,内部自动调用 this->destroy() 方法
164  */
165  virtual void on_connect_timeout(void) {}
166 
167  /**
168  * 当连接失败后的回调方法,在调用本方法后,内部自动调用 this->destroy() 方法
169  */
170  virtual void on_connect_failed(void) {}
171 
172  /**
173  * 当网络读超时时的回调方法
174  * @return {bool} 当读超时回调方法返回 true,则内部会继续读数据,如果
175  * 返回 false,则该连接将会被关闭,接着回调 on_disconnect() 虚方法
176  */
177  virtual bool on_read_timeout(void) { return false; }
178 
179  /**
180  * 对于连接成功后连接关闭后的回调方法,内部调用此方法后便立即回调
181  * destroy() 方法
182  */
183  virtual void on_disconnect(void) {};
184 
185  /**
186  * 当接收到 WEB 服务端的响应头时的回调方法
187  * @param header {const http_header&}
188  * @return {bool} 返回 false 则将会关闭连接,否则继续读
189  */
190  virtual bool on_http_res_hdr(const http_header& header)
191  {
192  (void) header;
193  return true;
194  }
195 
196  /**
197  * 当接收到 WEB 服务端的响应体时的回调方法,该方法可能会被多次回调
198  * 直到响应数据读完或出错
199  * @param data {char*} 读到的部分数据体内容
200  * @param dlen {size_t} 本次读到的 data 数据的长度
201  * @return {bool} 返回 false 则将会关闭连接,否则继续读
202  */
203  virtual bool on_http_res_body(char* data, size_t dlen)
204  {
205  (void) data;
206  (void) dlen;
207  return true;
208  }
209 
210  /**
211  * 当读完 HTTP 响应体或出错后的回调方法
212  * @param success {bool} 是否成功读完 HTTP 响应体数据
213  * @return {bool} 如果成功读完数据体后返回 false 则会关闭连接
214  */
215  virtual bool on_http_res_finish(bool success)
216  {
217  (void) success;
218  return true;
219  }
220 
221  /**
222  * 当 websocket 握手成功后的回调方法
223  * @return {bool} 返回 false 表示需要关闭连接,否则继续
224  */
225  virtual bool on_ws_handshake(void)
226  {
227  // 开始异步读 websocket 数据
228  this->ws_read_wait(0);
229  return true;
230  }
231 
232  /**
233  * 当 websocket 握手失败后的回调方法
234  * @param status {int} 服务器返回的 HTTP 响应状态码
235  */
236  virtual void on_ws_handshake_failed(int status) { (void) status; }
237 
238  /**
239  * 当读到一个 text 类型的帧时的回调方法
240  * @return {bool} 返回 true 表示继续读,否则则要求关闭连接
241  */
242  virtual bool on_ws_frame_text(void) { return true; }
243 
244  /**
245  * 当读到一个 binary 类型的帧时的回调方法
246  * @return {bool} 返回 true 表示继续读,否则则要求关闭连接
247  */
248  virtual bool on_ws_frame_binary(void) { return true; }
249 
250  /**
251  * 当读到一个关闭帧数据时的回调方法
252  */
253  virtual void on_ws_frame_closed(void) {}
254 
255  /**
256  * 在 websocket 通信方式,当读到数据体时的回调方法
257  * @param data {char*} 读到的数据地址
258  * @param dlen {size_t} 读到的数据长度
259  * @return {bool} 返回 true 表示继续读,否则则要求关闭连接
260  */
261  virtual bool on_ws_frame_data(char* data, size_t dlen)
262  {
263  (void) data;
264  (void) dlen;
265  return true;
266  }
267 
268  /**
269  * 当读完一帧数据时的回调方法
270  * @return {bool} 返回 true 表示继续读,否则则要求关闭连接
271  */
272  virtual bool on_ws_frame_finish(void) { return true; }
273 
274  /**
275  * 当收到 ping 数据包时的回调方法,当该回调方法返回后,如果用户没有将
276  * data 数据清理,则内部会自动给对端写入 pong 信息,如果用户将 data 缓
277  * 冲区清理掉,则该方法返回后不会给对接发 pong 信息
278  * @param data {string&} 读到的数据
279  */
280  virtual void on_ws_frame_ping(string& data)
281  {
282  (void) data;
283  }
284 
285  /**
286  * 收到 pong 数据时的回调方法
287  * @param data {string&} 读到的数据
288  */
289  virtual void on_ws_frame_pong(string& data)
290  {
291  (void) data;
292  }
293 
294 public:
295  /**
296  * 向 WEB 服务器发送 HTTP 请求,内部在发送后会自动开始读 HTTP 响应过程
297  * @param body {const void*} HTTP 请求的数据体,当为 NULL 时,内部会自
298  * 动采用 HTTP GET 方法
299  * @param len {size_t} body 非 NULL 时表示数据体的长度
300  */
301  void send_request(const void* body, size_t len);
302 
303  /**
304  * 与服务器进行 WEBSOCKET 握手
305  * @param key {const void*} 设置的 key 值
306  * @param len {size_t} key 值的长度
307  */
308  void ws_handshake(const void* key, size_t len);
309  void ws_handshake(const char* key = "123456789xxx");
310 
311  /**
312  * 当调用 ws_handshake() 时,内部会填充与 websocket 相关的请求头信息,
313  * 同时通过此回调告之调用者最终发给 websocket 服务器完整的请求头信息
314  */
315  virtual void ws_handshake_before(http_header& reqhdr)
316  {
317  (void) reqhdr;
318  }
319 
320  /**
321  * 开始异步读 websocket 数据
322  * @param timeout {int} 读超时时间,如果该值 <= 0,则不设读超时时间,
323  * 否则当读超时时,超时回调方法便会被调用;
324  * 注意:
325  * 该值与 open() 中的 rw_timeout 有所不同,open() 方法中的读超时仅限
326  * 定标准 HTTP IO 过程及 SSL 握手过程的读超时,而此处的读超时则用来
327  * 限制与 websocket 相关的读超时,这主要是考虑到 websocket 应用很多
328  * 都是长连接场景
329  */
330  void ws_read_wait(int timeout = 0);
331 
332  /**
333  * 异步发送一个 FRAME_TEXT 类型的数据帧
334  * @param data {char*} 内部可能因添加掩码原因被改变内容
335  * @param len {size_t} data 数据长度
336  * @return {bool}
337  */
338  bool ws_send_text(char* data, size_t len);
339 
340  /**
341  * 异步发送一个 FRAME_BINARY 类型的数据帧
342  * @param data {void*} 内部可能因添加掩码原因被改变内容
343  * @param len {size_t} data 数据长度
344  * @return {bool}
345  */
346  bool ws_send_binary(void* data, size_t len);
347 
348  /**
349  * 异步发送一个 FRAME_PING 类型的数据帧
350  * @param data {void*} 内部可能因添加掩码原因被改变内容
351  * @param len {size_t} data 数据长度
352  * @return {bool}
353  */
354  bool ws_send_ping(void* data, size_t len);
355 
356  /**
357  * 异步发送一个 FRAME_PONG 类型的数据帧
358  * @param data {void*} 内部可能因添加掩码原因被改变内容
359  * @param len {size_t} data 数据长度
360  * @return {bool}
361  */
362  bool ws_send_pong(void* data, size_t len);
363 
364 protected:
365  // @override dummy
366  bool open_callback(void) { return true; }
367 
368  // @override
369  bool timeout_callback(void);
370 
371  // @override
372  void close_callback(void);
373 
374  // @override
375  bool read_wakeup(void);
376 
377  // @override
378  bool read_callback(char* data, int len);
379 
380 protected:
381  unsigned status_;
383  int gzip_header_left_; // gzip 传输时压缩头部长度
385  bool unzip_; // 是否自动解压响应体数据
387  sslbase_conf* ssl_conf_; // 非空时才允许启用 SSL 功能
388  bool ssl_enable_; // 是否启用 SSL 功能
396  string* buff_;
397  zlib_stream* zstream_; // 解压对象
398  struct sockaddr_storage ns_addr_; // 所使用的 DNS 服务器地址
399  struct sockaddr_storage serv_addr_; // 所连接的应用服务器地址
400 
401  bool handle_connect(const ACL_ASTREAM_CTX* ctx);
402  bool handle_ssl_handshake(void);
403 
404  bool handle_res_hdr(int status);
405 
406  bool handle_res_body(char* data, int dlen);
407  bool res_plain(char* data, int dlen);
408  bool res_unzip(zlib_stream& zstream, char* data, int dlen);
409 
410  bool handle_res_body_finish(char* data, int dlen);
411  bool res_plain_finish(char* data, int dlen);
412  bool res_unzip_finish(zlib_stream& zstream, char* data, int dlen);
413 
414  bool handle_websocket(void);
415  bool handle_ws_data(void);
416  bool handle_ws_ping(void);
417  bool handle_ws_pong(void);
418  bool handle_ws_other(void);
419 
420 private:
421  static int connect_callback(const ACL_ASTREAM_CTX* ctx);
422  static int http_res_hdr_cllback(int status, void* ctx);
423  static int http_res_callback(int status, char* data, int dlen, void* ctx);
424 };
425 
426 } // namespace acl
virtual bool on_ws_handshake(void)
virtual void on_disconnect(void)
virtual bool on_read_timeout(void)
zlib_stream * zstream_
bool is_unzip_body(void) const
aio_socket_stream * conn_
HTTP_RES * http_res_
websocket * ws_out_
virtual void on_connect_failed(void)
virtual bool on_ws_frame_text(void)
sslbase_conf * get_ssl_conf(void) const
websocket * ws_in_
sslbase_conf * ssl_conf_
virtual void on_connect_timeout(void)
HTTP_HDR_RES * hdr_res_
bool open_callback(void)
virtual void ws_handshake_before(http_header &reqhdr)
virtual void on_ns_failed(void)
virtual bool on_ws_frame_finish(void)
virtual void on_ws_frame_closed(void)
virtual bool on_http_res_body(char *data, size_t dlen)
struct ACL_ASTREAM_CTX ACL_ASTREAM_CTX
Definition: acl_aio.h:98
bool is_enable_ssl(void) const
aio_handle & handle_
virtual void on_ws_frame_ping(string &data)
virtual void on_ws_frame_pong(string &data)
virtual void on_ws_handshake_failed(int status)
aio_socket_stream * get_conn(void) const
virtual bool on_ws_frame_data(char *data, size_t dlen)
virtual bool on_http_res_finish(bool success)
socket_stream * stream_
#define ACL_CPP_API
virtual bool on_http_res_hdr(const http_header &header)
virtual bool on_ws_frame_binary(void)
http_header * header_