nginx调用openssl函数源码分析_istan1ey的博客-程序员宅基地

技术标签: 网络  nginx  nginx开发学习汇总  网络协议  ssl  nginx反向代理  

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。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/realmardrid/article/details/122654229

智能推荐

使用doxygen查看文件包含关系图-程序员宅基地

http://blog.sina.com.cn/s/blog_4b976b2d0100jk0b.html转载于:https://www.cnblogs.com/hengli/archive/2013/01/22/2870888.html_doxygen 类图包含组合关系

解决 “python + opencv” 不能读取视频的问题-程序员宅基地

python + opencv 读取视频的三种情况:情况一:通过摄像头采集视频情况二:通过本地视频文件获取视频情况三:通过摄像头录制视频,再读取录制的视频摄像头采集、本地视频文件的读取、写视频文件,网上都有代码。我发现情况一和情况三都没有问题,大家注意读取自己通过摄像头录制的视频文件是没有问题的。但读取其他视频都会发现帧率为0(如果你获取视频的帧率并打印出来的话),并且

Swift基础入门知识学习(7)-字典-讲给你懂_swift 字典_MillVA的博客-程序员宅基地

Swift字典(dictionary) 用来存储无序的相同类型数据的集合,Swift 字典会强制检测元素的类型,如果类型不同则会报错。每個值(value)都屬於一個唯一的鍵(key),鍵作為字典中這個值的識別符號,所有鍵的型別也必須相同(鍵與值的型別不一定要相同)。因為字典內的值沒有順序,所以需要根據這個鍵(key)來找到需要的值(value)。宣告字典型別時,使用Dictionary<Key, Value>這個方式,這裡的Key表示字典裡鍵的型別,Value表示字典裡儲存的型別,如下://_swift 字典

Swiper组件(四)--Loop(环路)_swiper循环_神农尺的博客-程序员宅基地

文章目录looploopAdditionalSlidesloopedSlidesloopFillGroupWithBlankloopPreventsSlideloop(1)loop模式:会在原本slide前后复制若干个slide(默认一个)并在合适的时候切换,让Swiper看起来是循环的。(2)注意:loop模式在与free模式同用时会产生抖动,因为free模式下没有复制slide的时间点。<div class="swiper-box"> <div class="s_swiper循环

Maven导入依赖时jar包出现unknown_未解析的依赖项: 'com.auth0:java-jwt:jar:unknown-程序员宅基地

今天在一个项目中添加jar包依赖后导入发现jar包下边出现红色波浪线,而且jar包的版本显示unknown,显示错误:Cannot resolve com.alibaba.spring:spring-context-support:unknown解决在maven的本地仓库中,将出现unknown的文件夹和后缀有lastUpdated的文件全部删掉,再重新导入jar包后就成功了..._未解析的依赖项: 'com.auth0:java-jwt:jar:unknown

android - 布局优化-viewstub_viewbinding viewstub-程序员宅基地

ViewStub是Android中view的一种优化方案,它的目的是在不需要显示view的时候不去加载view,这样在view的创建时期,减少了加载的资源,优化了view。invisible 、gone 、 viewstub的区别:1、invisible view设置为invisible时,view在layout布局文件中会占用位置,但是view为不可见,该view还是会创建对象,会被初始......_viewbinding viewstub

随便推点

sed命令中关于-n的使用_为什么sed s不能加 -n-程序员宅基地

-n参数的作用是抑制自动输出首先看下一文件data内容执行sed 's/dog/cat/p' data命令查看效果执行sed -n 's/dog/cat/p' data命令效果再看一组命令对比sed '2s/dog/cat/p' data和sed -n '2s/dog/cat/p' data命令运行效果如下:..._为什么sed s不能加 -n

Objective-C学习-数组 NSArray 和 NSMutableArray-程序员宅基地

  在学习 Objective-C的过程中,感觉数组在这一个学习方面是一个比较大的应用域,所以写这篇随笔来总结一下。  首先声明的是,与C语言的数组不同,Objective-C的是数组只能用来存储对象,而不是存储int 、double、char等基础数据类型,一般要存这些基础数据类型,对于我这个刚学 Objective-C的新手来说,我的做法是将有关数字的基础类型 如 int 、do...

一文带你看清 HTTP 所有概念_proxy-revalidate-程序员宅基地

上一篇文章我们大致讲解了一下 HTTP 的基本特征和使用,大家反响很不错,那么本篇文章我们就来深究一下 HTTP 的特性。我们接着上篇文章没有说完的 HTTP 标头继续来介绍(此篇文章会介绍所有标头的概念,但没有深入底层)HTTP 标头先来回顾一下 HTTP1.1 标头都有哪几种HTTP 1.1 的标头主要分为四种,通用标头、实体标头、请求标头、响应标头,现在我们来对这几种标头进行介绍通用..._proxy-revalidate

Otto商品分类--LightGBM_keyerror: 'multi_logloss-mean-程序员宅基地

***模型训练部分***原始特征+tfidf特征1.工具import pandas as pdimport numpy as npimport lightgbm as lgbmform lightgbm.sklearn import LGBMClassifierfrom sklearn.model_selection import GridSearchCVimport..._keyerror: 'multi_logloss-mean

ssh远程登录服务器报错解决方案 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!_warning: remote host identification has changed! @-程序员宅基地

报错详情在命令行中通过ssh命令连接服务器时出现了如下哗啦哗啦的报错,一行行的@让我有点不知所措。@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@IT IS POSSIBLE THAT SOMEONE IS _warning: remote host identification has changed! @ @@@@@@@@@@@@@@@@@@@@@@@@@

非IT人员 制作精美ICO图标,文件夹 U盘 图标-程序员宅基地

分享一个好玩的电脑技巧。制作精美的U盘,文夹夹图标,ICO格式,可以在线上传普通照片制作,如想要更多的图标,直接在baidu中查找下载了。在线制作网址:http://www.ico.la/下图是本人U盘的图标,有个性吧,本人电脑中硬盘的图标:制作方法:把autorun.inf文件和icon.ico文件放在所要制作的盘的..._u盘图标ico制作 -baijiahao

推荐文章

热门文章

相关标签