In order to support HTTPS access, Web servers usually use the third-party library OpenSSL implementation, and for high performance using asynchronous event-driven model, so the connection socket is set as non-blocking type, this paper on the basis of Nginx SSL module, simplified extraction of its core framework, using object-oriented description. This paper analyzes the handshake, read/write, and close operations. Because these three operations are asynchronous, SSL_get_error is called to obtain the error code when the operation fails. SSL_ERROR_WANT_READ: The operation is not completed and needs to be continued in the next read event. SSL_ERROR_WANT_WRITE: The operation is not completed and needs to be continued in the next write event. SSL_ERROR_ZERO_RETURN: The connection is closed. Connection error The following example code uses libevent to implement the IO event driver, connection represents the ordinary connection class, assuming that the HTTP data logic has been handled, implemented in the member functions HANDle_read and handle_write, Virtual functions recv, SEND, and close call system API recv, SEND, and close respectively. Ssl_conn_t indicates the secure connection class. Data is decrypted after receiving and encrypted before sending. The decrypted operation is the same as connection, so SSL_conn_t inherits Connection and overrides recv, send, and close, where close calls shutdown. Asynchronous handshake When a connection is received on the SSL port, the handshake is performed first. Data can be sent or received only after the handshake succeeds. If the handshake fails and the first two error codes are returned, the handshake continues in the next operation.
1void ssl_conn_t::empty_handler(short ev)
2{
3}
4
5void ssl_conn_t::handshake_handler(short ev)
6{
7 handshake();
8}
9
10void ssl_conn_t::handshake()
11{
12 int ret = do_handshake();
13
14 switch(ret){
15 case OP_OK:
16 read_handler_ = &connection::handle_read;
17 write_handler_ = &connection::handle_write;
18 handle_read(EV_READ);
19 break;
20
21 }
22}
23
24int ssl_conn_t::do_handshake()
25{
26 ssl_clear_error();
27
28 int ret = SSL_do_handshake(ssl_);
29 if(1==ret){
30
31 return OP_OK;
32 }
33
34 int sslerr = SSL_get_error(ssl_,ret), err;
35 switch(sslerr){
36 case SSL_ERROR_WANT_READ:
37 read_handler_ = (io_handler)&ssl_conn_t::handshake_handler;
38 write_handler_ = (io_handler)&ssl_conn_t::empty_handler;
39 return OP_AGAIN;
40
41 case SSL_ERROR_WANT_WRITE:
42 read_handler_ = (io_handler)&ssl_conn_t::empty_handler;
43 write_handler_ = (io_handler)&ssl_conn_t::handshake_handler;
44 return OP_AGAIN;
45
46 }
47}
SSL_do_handshake = handshake_handler; SSL_do_handshake = handshake_handler; SSL_ERROR_WANT_READ = handshake_handler; Write the function pointer to empty_handler; If SSL_ERROR_WANT_WRITE is returned, change the read function pointer to empty_handler and the write function pointer to handshake_handler. The handshake_handler implementation continues processing the handshake in the read/write event, while empty_handler is an empty function that does nothing. SSL_ERROR_WANT_WRITE If the SSL_ERROR_WANT_WRITE command is returned, data will be read in the next write event. Therefore, the write function pointer should be changed to read data. When the read succeeds, the write function pin should be restored and a write event should be fired. For a write failure, if SSL_ERROR_WANT_READ is returned, the data will be continued in the next read event, so the read function pointer will be changed to write data, and when the write succeeds, the read function pointer will be restored and a read event will be fired. If you do not restore the read or write function pointer, write or read clutter occurs; After restoration, a read or write event is fired to continue the IO event and prevent the read and write from starving.
1ssize_t ssl_conn_t::recv(void *buf,size_t len)
2{
3 ssl_clear_error();
4
5 int ret = SSL_read(ssl_,buf,len);
6 if(ret>0){
7 if(old_write_handler_){
8 write_handler_ = old_write_handler_;
9 old_write_handler_ = NULL;
10 active_event(false);
11 }
12 return ret;
13 }
14
15 int sslerr = SSL_get_error(ssl_,ret), err;
16 switch(sslerr){
17 case SSL_ERROR_WANT_READ:
18 return OP_AGAIN;
19
20 case SSL_ERROR_WANT_WRITE:
21 if(NULL==old_write_handler_){
22 old_write_handler_ = write_handler_;
23 write_handler_ = (io_handler)&ssl_conn_t::write_handler;
24 }
25 return OP_AGAIN;
26
27 }
28}
29
30void ssl_conn_t::write_handler(short ev)
31{
32 (this->*read_handler_)(EV_WRITE);
33}
34
35ssize_t ssl_conn_t::send(const void *buf,size_t len)
36{
37 ssl_clear_error();
38
39 int ret = SSL_write(ssl_,buf,len);
40 if(ret>0){
41 if(old_read_handler_){
42 read_handler_ = old_read_handler_;
43 old_read_handler_ = NULL;
44 active_event(true);
45 }
46 return ret;
47 }
48
49 int sslerr = SSL_get_error(ssl_,ret), err;
50 switch(sslerr){
51 case SSL_ERROR_WANT_WRITE:
52 return OP_AGAIN;
53
54 case SSL_ERROR_WANT_READ:
55 if(NULL==old_read_handler_){
56 old_read_handler_ = read_handler_;
57 read_handler_ = (io_handler)&ssl_conn_t::read_handler;
58 }
59 return OP_AGAIN;
60
61 }
62}
63
64void ssl_conn_t::read_handler(short ev)
65{
66 (this->*write_handler_)(EV_READ);
67}
The recV calls SSL_read. If this fails and SSL_ERROR_WANT_WRITE is returned, the old write function pointer is saved, and the write_handler pointer is changed to write_handler. The write_handler implementation continues to read data during the write event. Send calls SSL_write. If SSL_ERROR_WANT_READ fails and SSL_ERROR_WANT_READ is returned, the old read function pointer is saved and changed to read_handler, which continues writing data in the read event. Asynchronous closing When the handshake or read/write fails due to a closed or faulty connection, the connection is closed, and if this fails and SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE is returned, the closure is continued on the next read or write event. In this case, to receive the protocol exit packet sent by the other party, exit completely, wait 30 seconds and then close, if the timeout, directly close.
1void ssl_conn_t::shutdown(bool is_timeout/*=false*/)
2{
3if (do_shutdown(is_timeout) ! = OP_AGAIN)
4 delete this;
5}
6
7int ssl_conn_t::do_shutdown(bool is_timeout)
8{
9 int ret,mode;
10
11 if(is_timeout){
12 mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
13 SSL_set_quiet_shutdown(ssl_,1);
14 }else{
15
16 }
17 SSL_set_shutdown(ssl_,mode);
18
19 ssl_clear_error();
20
21 ret = SSL_shutdown(ssl_);
22 if(1==ret)
23 return OP_OK;
24
25 int sslerr = SSL_get_error(ssl_,ret), err;
26 switch(sslerr){
27
28 case SSL_ERROR_WANT_READ:
29 case SSL_ERROR_WANT_WRITE:
30 read_handler_ = (io_handler)&ssl_conn_t::shutdown_handler;
31 write_handler_ = (io_handler)&ssl_conn_t::shutdown_handler;
32
33 if(SSL_ERROR_WANT_READ==sslerr){
34 struct timeval tv;
35 tv.tv_sec = 30,tv.tv_usec = 0;
36 add_event(true,tv);
37 }
38 return OP_AGAIN;
39
40 }
41}
42
43void ssl_conn_t::shutdown_handler(short ev)
44{
45 shutdown(ev&EV_TIMEOUT);
46}
If this fails and SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE is returned, change the read/write function pointer to shutdown_handler. The shutdown_handler implementation continues to close processing in read and write events.