NGINX部分
在ngx_http_init_connection中把recv→handler设置为ngx_http_ssl_handshake。
然后将这个读时间加入到epoll中,主要是分析handshake函数。
receive client hello
static void
ngx_http_ssl_handshake(ngx_event_t *rev)
{
...
n = recv(c->fd, (char *) buf, size, MSG_PEEK);
//判断协议
if (n == 1) {
if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
// 获取loc conf和server conf
clcf = ngx_http_get_module_loc_conf(hc->conf_ctx,
ngx_http_core_module);
if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
ngx_http_ssl_module);
// 调用该函数生成ssl
if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER != NGX_OK)
{
ngx_http_close_connection(c);
return;
}
}
}
...
}
ngx_ssl_create_connection
ngx_int_t
ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
{
...
sc->session_ctx = ssl->ctx;
sc->connection = SSL_new(ssl->ctx);
if (sc->connection == NULL) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");
return NGX_ERROR;
}
if (SSL_set_fd(sc->connection, c->fd) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");
return NGX_ERROR;
}
...
}
first收到client hello之后,完成初始化。
然后调用ngx_ssl_handshake函数,里面会调用openssl的ssl_do_handshake。
ngx_int_t
ngx_ssl_handshake(ngx_connection_t *c)
{
...
n = ngx_ssl_handshake_early_data(c);
n = SSL_do_handshake(c->ssl->connection);
...
}
do handshake的时候调用的是openssl的async job的库
OPENSSL部分
ASYNC JOB
int SSL_do_handshake(SSL *s)
{
...
if (SSL_in_init(s) || SSL_in_before(s)) {
if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) {
struct ssl_async_args args;
args.s = s;
ret = ssl_start_async_job(s, &args, ssl_do_handshake_intern);
} else {
ret = s->handshake_func(s);
}
}
...
}
int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret,
int (*func)(void *), void *args, size_t size)
{
...
/* Start a new job */
if ((ctx->currjob = async_get_pool_job()) == NULL)
return ASYNC_NO_JOBS;
...
}
static ASYNC_JOB *async_get_pool_job(void) {
...
if (job == NULL) {
/* Pool is empty */
if ((pool->max_size != 0) && (pool->curr_size >= pool->max_size))
return NULL;
job = async_job_new();
if (job != NULL) {
if (! async_fibre_makecontext(&job->fibrectx)) {
async_job_free(job);
return NULL;
}
pool->curr_size++;
}
}
...
}
先get context做初始化,然后malloc一个stack,创建堆栈把函数放进去。
makecontext创建调用运行函数async_start_func,函数本身是使用当前job中的func。
int async_fibre_makecontext(async_fibre *fibre)
{
fibre->env_init = 0;
if (getcontext(&fibre->fibre) == 0) { //初始化当前ucontext
fibre->fibre.uc_stack.ss_sp = OPENSSL_malloc(STACKSIZE);
if (fibre->fibre.uc_stack.ss_sp != NULL) {
fibre->fibre.uc_stack.ss_size = STACKSIZE;
fibre->fibre.uc_link = NULL;
makecontext(&fibre->fibre, async_start_func, 0);
return 1;
}
} else {
fibre->fibre.uc_stack.ss_sp = NULL;
}
return 0;
}
pause job最关键的是swapcontext,在func中一旦被调用的话,就可以立即切换栈信息。
切回start_job的主函数,根据job→status=ASYNC_JOB_PAUSING来返回
int ASYNC_pause_job(void)
{
...
if (!async_fibre_swapcontext(&job->fibrectx,
&ctx->dispatcher, 1)) {
ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
return 0;
}
...
}
切回主函数后可以发现start job是for死循环任务,会根据job的状态进行返回
int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret,
int (*func)(void *), void *args, size_t size)
{
...
for (;;) {
if (ctx->currjob != NULL) {
if (ctx->currjob->status == ASYNC_JOB_PAUSING) {
*job = ctx->currjob;
ctx->currjob->status = ASYNC_JOB_PAUSED;
ctx->currjob = NULL;
return ASYNC_PAUSE;
}
if (ctx->currjob->status == ASYNC_JOB_PAUSED) {
ctx->currjob = *job;
/* Resume previous job */
if (!async_fibre_swapcontext(&ctx->dispatcher,
&ctx->currjob->fibrectx, 1)) {
ASYNCerr(ASYNC_F_ASYNC_START_JOB,
ASYNC_R_FAILED_TO_SWAP_CONTEXT);
goto err;
}
continue;
}
...
}
static int ssl_start_async_job(SSL *s, struct ssl_async_args *args,
int (*func) (void *))
{
...
switch (ASYNC_start_job(&s->job, s->waitctx, &ret, func, args,
sizeof(struct ssl_async_args))) {
case ASYNC_ERR:
s->rwstate = SSL_NOTHING;
SSLerr(SSL_F_SSL_START_ASYNC_JOB, SSL_R_FAILED_TO_INIT_ASYNC);
return -1;
case ASYNC_PAUSE:
s->rwstate = SSL_ASYNC_PAUSED;
return -1;
}
...
}
返回的这个状态码,在nginx里面接到就是SSL_ERROR_WANT_ASYNC。