Web服务器为了支持https访问,通常会使用第三方库openssl实现,而且为了高性能采用异步事件驱动模型,因此连接套接字被设为非阻塞类型,本文在nginx ssl模块的基础上,简化提取它的核心框架,使用面向对象的方式描述,从握手、读写和关闭3个方面进行了分析,由于这3个操作都是异步的,因此操作失败后要调用SSL_get_error来获取错误码,有如下4种情况。
● SSL_ERROR_WANT_READ:操作没完成,需要在下一次读事件中继续
● SSL_ERROR_WANT_WRITE:操作没完成,需要在下一次写事件中继续
● SSL_ERROR_ZERO_RETURN:连接正常关闭
● 其它:连接出错
下面的示例代码使用了libevent实现IO事件驱动,connection表示普通连接类,假设已经处理好了http数据的逻辑,实现在成员函数handle_read和handle_write内,虚函数recv、send和close分别调用系统的API recv、send和close;ssl_conn_t表示安全连接类,由于只是IO处理不同:收到数据后解密,发送数据前加密。解密后的操作和connection是一样的,因此ssl_conn_t继承connection,重写了recv、send和close,其中close调用了shutdown。
异步握手
当在SSL端口接受到连接时,首先要进行握手,握手成功后才能收发数据,如果握手失败而且返回前2种错误码,那么要在下一次操作中继续握手。
1
void ssl_conn_t::empty_handler(short ev)
2![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
3
}
4![](/Images/OutliningIndicators/None.gif)
5
void ssl_conn_t::handshake_handler(short ev)
6![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
7
handshake();
8
}
9![](/Images/OutliningIndicators/None.gif)
10
void ssl_conn_t::handshake()
11![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
12
int ret = do_handshake();
13![](/Images/OutliningIndicators/InBlock.gif)
14![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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![](/Images/OutliningIndicators/None.gif)
24
int ssl_conn_t::do_handshake()
25![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
26
ssl_clear_error();
27![](/Images/OutliningIndicators/InBlock.gif)
28
int ret = SSL_do_handshake(ssl_);
29![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if(1==ret)
{
30
![](http://www.cppblog.com/Images/dot.gif)
31
return OP_OK;
32
}
33
34
int sslerr = SSL_get_error(ssl_,ret), err;
35![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
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
![](http://www.cppblog.com/Images/dot.gif)
46
}
47
}
以上do_handshake是核心函数,调用了SSL_do_handshake来实现握手,当SSL_do_handshake失败时,如果返回SSL_ERROR_WANT_READ,就改变读函数指针为handshake_handler,写函数指针为empty_handler;如果返回SSL_ERROR_WANT_WRITE,就改变读函数指针为empty_handler,写函数指针为handshake_handler。handshake_handler实现在读写事件中继续处理握手,而empty_handler是个空函数,什么也不做。
异步读写
对于读操作失败,如果返回SSL_ERROR_WANT_WRITE,那么说明要在下一次写事件中继续读数据,因此要改变写函数指针,使其读数据,当读成功后,要还原写函数针,并激发一次写事件;对于写操作失败,如果返回SSL_ERROR_WANT_READ,那么说明要在下一次读事件中继续写数据,因此要改变读函数指针,使其写数据,当写成功后,要还原读函数指针,并激发一次读事件。如果不还原读或写函数指针,那么会发生写或读混乱;还原后,要激发一次读或写事件,这是为了延续IO事件的进行,防止读写饿死。
1
ssize_t ssl_conn_t::recv(void *buf,size_t len)
2![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
3
ssl_clear_error();
4![](/Images/OutliningIndicators/InBlock.gif)
5
int ret = SSL_read(ssl_,buf,len);
6![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if(ret>0)
{
7![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
15
int sslerr = SSL_get_error(ssl_,ret), err;
16![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
switch(sslerr)
{
17
case SSL_ERROR_WANT_READ:
18
return OP_AGAIN;
19![](/Images/OutliningIndicators/InBlock.gif)
20
case SSL_ERROR_WANT_WRITE:
21![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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
![](http://www.cppblog.com/Images/dot.gif)
27
}
28
}
29![](/Images/OutliningIndicators/None.gif)
30
void ssl_conn_t::write_handler(short ev)
31![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
32
(this->*read_handler_)(EV_WRITE);
33
}
34![](/Images/OutliningIndicators/None.gif)
35
ssize_t ssl_conn_t::send(const void *buf,size_t len)
36![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
37
ssl_clear_error();
38![](/Images/OutliningIndicators/InBlock.gif)
39
int ret = SSL_write(ssl_,buf,len);
40![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if(ret>0)
{
41![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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![](/Images/OutliningIndicators/InBlock.gif)
49
int sslerr = SSL_get_error(ssl_,ret), err;
50![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
switch(sslerr)
{
51
case SSL_ERROR_WANT_WRITE:
52
return OP_AGAIN;
53![](/Images/OutliningIndicators/InBlock.gif)
54
case SSL_ERROR_WANT_READ:
55![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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
![](http://www.cppblog.com/Images/dot.gif)
61
}
62
}
63![](/Images/OutliningIndicators/None.gif)
64
void ssl_conn_t::read_handler(short ev)
65![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
66
(this->*write_handler_)(EV_READ);
67
}
以上recv调用SSL_read,如果失败并且返回SSL_ERROR_WANT_WRITE,就保存老的写函数指针,改变写函数指针为write_handler,write_handler实现在写事件中继续读数据;send调用SSL_write,如果失败并且返回SSL_ERROR_WANT_READ,就保存老的读函数指针,改变读函数指针为read_handler,read_handler实现在读事件中继续写数据。
异步关闭
当握手或读写因连接关闭或出错而失败时,就要关闭连接了,如果失败并且返回SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE,那么要在下一次读或写事件中继续关闭。在这里,为了收到对方发送的协议退出包而完全退出,等待30秒再关闭,如果超时就直接关闭。
1![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
void ssl_conn_t::shutdown(bool is_timeout/**//*=false*/)
2![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
if (do_shutdown(is_timeout) != OP_AGAIN)
4
delete this;
5
}
6![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
7
int ssl_conn_t::do_shutdown(bool is_timeout)
8![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
9
int ret,mode;
10![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
11![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
if(is_timeout)
{
12
mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
13
SSL_set_quiet_shutdown(ssl_,1);
14![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
}else
{
15
![](http://www.cppblog.com/Images/dot.gif)
16
}
17
SSL_set_shutdown(ssl_,mode);
18![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
19
ssl_clear_error();
20![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
21
ret = SSL_shutdown(ssl_);
22
if(1==ret)
23
return OP_OK;
24![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
25
int sslerr = SSL_get_error(ssl_,ret), err;
26![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
switch(sslerr)
{
27
![](http://www.cppblog.com/Images/dot.gif)
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![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
33![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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
![](http://www.cppblog.com/Images/dot.gif)
40
}
41
}
42![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
43
void ssl_conn_t::shutdown_handler(short ev)
44![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
45
shutdown(ev&EV_TIMEOUT);
46
}
以上do_shutdown是核心函数,调用SSL_shutdown实现,如果失败并且返回SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE,就改变读写函数指针为shutdown_handler,shutdown_handler实现在读写事件中继续关闭处理。
posted on 2014-04-11 17:26
春秋十二月 阅读(13615)
评论(0) 编辑 收藏 引用 所属分类:
Network