apache(httpd-2.2.14) mod_ssl源碼分析之三(mod_ssl處理流程分析)

      由於過年家裏出了些事情博客一直沒有更新,再加上公司的Apache項目要推遲,好久沒有寫關於Apache的文章了,但是前幾天張老師在我的博客文章留言讓我很是感到榮幸,我想我會繼續爲Apache的探索做出自己的一份力。

      在apache(httpd-2.2.14) mod_ssl源碼分析之二中我們初步定位了mod_ssl的內部是怎樣處理請求的,我將繼續就mod_ssl的處理流程做更加詳細的分析。

      在apache(httpd-2.2.14) mod_ssl源碼分析之二中我們看到了一個樹形的處理流程圖,其中最重要的就是它的分支點ssl_hook_pre_connection,這個掛鉤的作用是:當Apache的socket接收到請求時,會爲處理請求分配一個新的子進程,core核心模塊中的Connection.c文件的ap_process_connection()會調用掛鉤函數ap_hook_pre_connection,而mod_ssl剛好實現了該掛鉤的具體實例來判斷是否需要建立openssl連接,如果不需要則會按照http的處理流程在讀取請求的時候只調用core_in這個過濾器對網絡中的http明文數據進行讀取,如果需要建立openssl的連接則會調用ssl_init_ssl_connection函數對ssl連接進行初始化操作,而在ssl_init_ssl_connection這個函數中最最重要的就是將mod_ssl的過濾器加入到Apache的過濾器列表中,filter_ctx->pOutputFilter   = ap_add_output_filter(cssl_io_filter,filter_ctx, NULL, c)和ssl_io_input_add_filter(filter_ctx, c, ssl) 這兩個函數也正是這個作用,在這個階段完成以後,mod_ssl就可以休息一會,直到Http_core.c這個文件中的函數ap_process_http_connection(conn_rec *c)被調用纔會讓真正的處理開始,ap_process_http_connection(conn_rec *c)這個函數的作用是處理http請求連接,在這個函數內的函數ap_read_request(conn_rec *conn)纔是真正的重頭戲,在這個函數外有一個while語句來循環地讀取socket接受到得請求,ap_read_request(conn_rec *conn)調用read_request_line(request_rec *r, apr_bucket_brigade *bb)函數...一層層的調用到一個叫做ap_rgetline_core(char **s, apr_size_t n,apr_size_t *read, request_rec *r, int fold, apr_bucket_brigade *bb)的函數。

在這個函數中存在一個for循環,該循環是讀取多行請求的循環,換句話說就是一個請求的行數和該循環的執行次數是相同的,在for的內部有一個叫做ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE,APR_BLOCK_READ, 0)的函數,下面我們來細說這個函數。

      在介紹這個函數之前我們要先理解一下什麼是存儲段組、過濾器以及存儲段組和過濾器之間的關係。

      存儲段組:Apache從客戶端接受或返回給客戶端的數據稱之爲外部數據,爲了存儲外部數據,Apache引入了存儲段和存儲段組的概念,這是爲了減少客戶端數據的不確定性而引進的一種新的存儲機制,其中存儲段是Apache中保存外部數據的最基本單位,Apache內部所有的數據包括核心生成的數據、過濾器中傳輸的數據等等,他們都必須轉換爲存儲段的形式,多個存儲段又可以通過存儲段組進行統一的管理,每一個存儲段組中包含一個或多個存儲段。

      過濾器:我所理解的過濾器就是一個物品提煉與加工工廠,以提煉工廠爲例,假如我們引進了一批海水要提取鈉離子,首先第一步就是先要將海水提純,接下來要烘乾,提取粗鹽,最終提取出鈉離子,而這每一道工序都可以看做是一個過濾器,這些過濾器的職能是單一的,只有將它們所有的過濾器變成一個生產線纔是最有意義的。如果我的工廠想要轉變生產策略,想要提取氯離子而不是鈉離子了,怎麼樣的變動才能使我這條生產線的花費最少並且更具靈活性呢,就是將每一個過濾器進行靈活的配置,用到哪個就安上哪個,不用的就將它去掉,這也就是Apache巧妙的構思所在,比如我們前面反覆提到的ap_hook_pre_connection函數就是判斷是否將mod_ssl過濾器設備加入到流水線的過程,如果不需要mod_ssl的過濾器ssl_filter,則會按照http的處理流程在讀取請求的時候只調用core_in這個過濾器對網絡中的http明文數據進行讀取,如果需要建立openssl的連接則會調用ssl_init_ssl_connection函數對ssl連接進行初始化操作,而在ssl_init_ssl_connection這個函數中最最重要的就是將mod_ssl的過濾器加入到Apache的過濾器列表中,這樣的判斷使模塊與模塊間的耦合性降低,設計相當的巧妙!

      過濾器的種類有很多:比如資源過濾器、內容過濾器、協議過濾器等等,詳見張中慶老師的《Apache源代碼全景分析第一卷:體系結構與核心模塊》的第7章。

       存儲段組和過濾器之間的關係:正常情況下,存儲段總是和過濾器關聯在一起的,因此當存儲段組在過濾器之間進行數據傳遞時,對於某一個過濾器而言,就是能夠獲取下一個過濾器的內容,只有獲取存儲段組的內容,纔有可能進行過濾處理。

       繼續細說ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE,APR_BLOCK_READ, 0)這個函數,這個函數的作用是用於獲取下一個過濾器的存儲組的內容,函數只是在確實具有下一個過濾器的時候才直接調用過濾器本身所具有的輸入過濾器函數,ap_get_brigade函數通常用於輸入過濾器,在HTTP請求處理的時候,Apache核心並不會直接去讀取網絡中的請求數據,實際上它只是建立一個空的存儲段組,然後將此存儲段組傳遞給輸入過濾器鏈表中的第一個過濾器,由該過濾器在存儲段中填入實際的請求,然後第一個過濾器在做簡單處理之後會繼續調用ap_get_brigade函數,將處理過的存儲段組傳遞給第二個過濾器,直到最後一個過濾器。

       如果在ap_hook_pre_connection階段確實要加入mod_ssl的過濾器ssl_filter,那麼Apache第一個調用的就是ssl_filter過濾器,在該過濾器中調用ssl_io_filter_connect(inctx->filter_ctx))判斷該ssl連接是否已經進行了握手,如果沒有則調用SSL_accept(filter_ctx->pssl))函數進行握手操作,如果握手已經完畢則直接通過函數SSL_read(inctx->filter_ctx->pssl, buf + bytes, wanted - bytes)進行密文請求的讀取,但是,無論是握手操作還是讀取密文(解密)操作都會調用一個非常非常重要的函數,它就是static int bio_filter_in_read(BIO *bio, char *in, int inlen),在這個函數中還是調用ap_get_brigade函數,然後通過函數ap_get_brigade繼續調用core_filter,在此過程中只用到了這兩個過濾器,其中的ssl_filter過濾器就是用於openssl的握手和加密解密過程,而core_filter是過濾器鏈表中的最後一個過濾器,因此無法繼續傳遞,只能從網絡中讀取請求數據,然後填充到傳遞過來的存儲段組,最後返回到核心進行進一步處理。

 

待續...

發佈了18 篇原創文章 · 獲贊 5 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章