<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ### nginx支持ssl簡介[](http://tengine.taobao.org/book/chapter_12.html#nginxssl "永久鏈接至標題") nginx-1.2.0編譯時默認是不支持ssl協議的,需要通過編譯指令來開啟對其支持: [](http:// "點擊提交Issue,反饋你的意見...") ./configure --with-http_ssl_module 在nginx源碼中,ssl相關代碼用宏定義變量NGX_HTTP_SSL來控制是否開啟。這給我們查找和閱讀ssl相關代碼帶來了方便,如下: ssl協議工作在tcp協議與http協議之間。nginx在支持ssl協議時,需要注意三點,其他時候只要正常處理http協議即可: 1. tcp連接建立時,在tcp連接上建立ssl連接 1. tcp數據接收后,將收到的數據解密并將解密后的數據交由正常http協議處理流程 1. tcp數據發送前,對(http)數據進行加密,然后再發送 以下章節將分別介紹這三點。 [](http:// "點擊提交Issue,反饋你的意見...") ### ssl連接建立(ssl握手)[](http://tengine.taobao.org/book/chapter_12.html#ssl-ssl "永久鏈接至標題") [](http:// "點擊提交Issue,反饋你的意見...") #### 對ssl連接建立的準備[](http://tengine.taobao.org/book/chapter_12.html#ssl "永久鏈接至標題") 更具ssl協議規定,在正式發起數據收發前,需要建立ssl連接,連接建立過程既ssl握手。nginx在創建和初始化http請求階段的同時為tcp連接建立做準備,主要流程在ngx_http_init_request函數中實現: [](http:// "點擊提交Issue,反饋你的意見...") static void ngx_http_init_request(ngx_event_t *rev) { ... #if (NGX_HTTP_SSL) { ngx_http_ssl_srv_conf_t *sscf; sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); if (sscf->enable || addr_conf->ssl) { /* c->ssl不為空時,表示請求復用長連接(已經建立過ssl連接) */ if (c->ssl == NULL) { c->log->action = "SSL handshaking"; /* * nginx.conf中開啟ssl協議(listen 443 ssl;), * 卻沒用設置服務器證書(ssl_certificate <certificate_path>;) */ if (addr_conf->ssl && sscf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); ngx_http_close_connection(c); return; } /* * 創建ngx_ssl_connection_t并初始化 * openssl庫中關于ssl連接的初始化 */ if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) != NGX_OK) { ngx_http_close_connection(c); return; } rev->handler = ngx_http_ssl_handshake; } /* ssl加密的數據必須讀到內存中 */ r->main_filter_need_in_memory = 1; } } #endif ... } ngx_http_init_request大部分流程已經在前面章節分析過了,這個函數主要負責初始化http請求,此時并沒有實際解析http請求。若發來的請求是經由ssl協議加密的,直接解析http請求就會出錯。ngx_http_init_request中ssl協議相關處理流程: 1,首先判斷c->ssl是否為空。若不為空:說明這里是http長連接的情況,ssl連接已經在第一個請求進入時建立了。這里只要復用這個ssl連接即可,跳過ssl握手階段。 2.(1),若c->ssl為空:需要進行ssl握手來建立連接。此時調用ngx_ssl_create_connection為ssl連接建立做準備。 ngx_ssl_create_connection 簡化代碼如下: [](http:// "點擊提交Issue,反饋你的意見...") ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) { ngx_ssl_connection_t *sc; /* ngx_ssl_connection_t是nginx對ssl連接的描述結構,記錄了ssl連接的信息和狀態 */ sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t)); sc->buffer = ((flags & NGX_SSL_BUFFER) != 0); /* 創建openssl庫中對ssl連接的描述結構 */ sc->connection = SSL_new(ssl->ctx); /* 關聯(openssl庫)ssl連接到tcp連接對應的socket */ SSL_set_fd(sc->connection, c->fd); if (flags & NGX_SSL_CLIENT) { /* upstream中發起對后端的ssl連接,指明nginx ssl連接是客戶端 */ SSL_set_connect_state(sc->connection); } else { /* 指明nginx ssl連接是服務端 */ SSL_set_accept_state(sc->connection); } /* 關聯(openssl庫)ssl連接到用戶數據(當前連接c) */ SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c); c->ssl = sc; return NGX_OK; } 2.(2),設置連接讀事件處理函數為ngx_http_ssl_handshake,這將改變后續處理http請求的正常流程為:先進行ssl握手,再正常處理http請求。 3,標明當前待發送的數據須在內存中,以此可以讓ssl對數據進行加密。由于開啟了ssl協議,對發送出去的數據要進行加密,這就要求待發送的數據必須在內存中。 標識r->main_filter_need_in_memory為1,可以讓后續數據發送前,將數據讀取到內存中 (防止在文件中的數據通過sendfile直接發送出去,而沒有加密)。 [](http:// "點擊提交Issue,反饋你的意見...") #### 實際ssl握手階段[](http://tengine.taobao.org/book/chapter_12.html#id18 "永久鏈接至標題") 由于在ngx_http_init_request中將連接讀事件處理函數設置成ngx_http_ssl_handshake,當連接中有可讀數據時,將會進入ngx_http_ssl_handshake來處理(若未開啟ssl,將進入ngx_http_process_request_line直接解析http請求) 在ngx_http_ssl_handshake中,來進行ssl握手: 1,首先判斷連接是否超時,如果超時則關閉連接 [](http:// "點擊提交Issue,反饋你的意見...") static void ngx_http_process_request(ngx_http_request_t *r) { if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } 2,首字節預讀:從tcp連接中查看一個字節(通過MSG_PEEK查看tcp連接中數據,但不會實際讀取該數據),若tcp連接中沒有準備好的數據,則重新添加讀事件退出等待新數據到來。 [](http:// "點擊提交Issue,反饋你的意見...") n = recv(c->fd, (char *) buf, 1, MSG_PEEK); if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); } if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } return; } 3,首字節探測:若成功查看1個字節數據,通過該首字節來探測接受到的數據是ssl握手包還是http數據。根據ssl協議規定,ssl握手包的首字節中包含有ssl協議的版本信息。nginx根據此來判斷是進行ssl握手還是返回正常處理http請求(實際返回應答400 BAD REQUEST)。 [](http:// "點擊提交Issue,反饋你的意見...") if (n == 1) { if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "https ssl handshake: 0x%02Xd", buf[0]); /* * 調用ngx_ssl_handshake函數進行ssl握手,連接雙方會在ssl握手時交換相 * 關數據(ssl版本,ssl加密算法,server端的公鑰等) 并正式建立起ssl連接。 * ngx_ssl_handshake函數內部對openssl庫進行了封裝。 * 調用SSL_do_handshake()來進行握手,并根據其返回值判斷ssl握手是否完成 * 或者出錯。 */ rc = ngx_ssl_handshake(c); /* * ssl握手可能需要多次數據交互才能完成。 * 如果ssl握手沒有完成,ngx_ssl_handshake會根據具體情況(如需要讀取更 * 多的握手數據包,或者需要發送握手數據包)來重新添加讀寫事件 */ if (rc == NGX_AGAIN) { if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); } c->ssl->handler = ngx_http_ssl_handshake_handler; return; } /* * 若ssl握手完成或者出錯,ngx_ssl_handshake會返回NGX_OK或者NGX_ERROR, 然后ngx_http_ssl_handshake調用 * ngx_http_ssl_handshake_handler以繼續處理 */ ngx_http_ssl_handshake_handler(c); return; } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "plain http"); r->plain_http = 1; } } 需要特別注意,如果ssl握手完成,ngx_ssl_handshake會替換連接的讀寫接口。這樣,后續需要讀寫數據時,替換的接口會對數據進行加密解密。詳細代碼見下: [](http:// "點擊提交Issue,反饋你的意見...") ngx_int_t ngx_ssl_handshake(ngx_connection_t *c) { n = SSL_do_handshake(c->ssl->connection); /* 返回1表示ssl握手成功 */ if (n == 1) { ... c->ssl->handshaked = 1; c->recv = ngx_ssl_recv; c->send = ngx_ssl_write; c->recv_chain = ngx_ssl_recv_chain; c->send_chain = ngx_ssl_send_chain; return NGX_OK; } ... } 4,探測為http協議:正常的http協議包處理直接調用ngx_http_process_request_line處理http請求,并將讀事件處理函數設置成ngx_http_process_request_line。(實際處理結果是向客戶端返回400 BAD REQUET,在ngx_http_process_request中又對r->plain_http標志的單獨處理。) [](http:// "點擊提交Issue,反饋你的意見...") c->log->action = "reading client request line"; rev->handler = ngx_http_process_request_line; ngx_http_process_request_line(rev); } /* end of ngx_http_process_request() */ 5,當ssl握手成功或者出錯時,調用ngx_http_ssl_handshake_handler函數。 5.(1),若ssl握手完成 (c->ssl->handshaked由ngx_ssl_handshake()確定握手完成后設為1),設置讀事件處理函數為ngx_http_process_request_line,并調用此函數正常處理http請求。 5.(2),若ssl握手沒完成(則說明ssl握手出錯),則返回400 BAD REQUST給客戶端。 至此,ssl連接已經建立,此后在ngx_http_process_request中會讀取數據并解密然后正常處理http請求。 [](http:// "點擊提交Issue,反饋你的意見...") static void ngx_http_ssl_handshake_handler(ngx_connection_t *c) { ngx_http_request_t *r; if (c->ssl->handshaked) { /* * The majority of browsers do not send the "close notify" alert. * Among them are MSIE, old Mozilla, Netscape 4, Konqueror, * and Links. And what is more, MSIE ignores the server's alert. * * Opera and recent Mozilla send the alert. */ c->ssl->no_wait_shutdown = 1; c->log->action = "reading client request line"; c->read->handler = ngx_http_process_request_line; /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler; ngx_http_process_request_line(c->read); return; } r = c->data; ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST); return; } [](http:// "點擊提交Issue,反饋你的意見...") #### ssl協議接受數據[](http://tengine.taobao.org/book/chapter_12.html#id19 "永久鏈接至標題") ngx_http_process_request中處理http請求,需要讀取和解析http協議。而實際數據讀取是通過c->recv()函數來讀取的,此函數已經在ngx_ssl_handshake中被替換成ngx_ssl_recv了。 ngx_ssl_recv函數中調用openssl庫函數SSL_read()來讀取并解密數據,簡化后如下: [](http:// "點擊提交Issue,反饋你的意見...") ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size) { ... n = SSL_read(c->ssl->connection, buf, size); ... return n; } [](http:// "點擊提交Issue,反饋你的意見...") #### ssl協議發送數據[](http://tengine.taobao.org/book/chapter_12.html#id20 "永久鏈接至標題") 當nginx發送數據時,如使用ngx_output_chain函數發送緩存的http數據緩存鏈時,通過調用c->send_chain()來發送數據。這個函數已經在ngx_ssl_handshake中被設置成ngx_ssl_send_chain了。ngx_ssl_send_chain會進一步調用ngx_ssl_write。而ngx_ssl_write調用openssl庫SSL_write函數來加密并發送數據。 [](http:// "點擊提交Issue,反饋你的意見...") /* ngx_output_chain * -> .. * -> ngx_chain_writer * -> c->send_chain (ngx_ssl_send_chain) * -> ngx_ssl_write */ ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) { ... n = SSL_write(c->ssl->connection, data, size); ... return n; }
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看