Coyote for Http11: org.apache.coyote.http11

edagarli發表於2014-03-06

概述

這個包支援http1.1協議,內部分為三類:ARP、NIO、普通http,這裡只對最基本的普通http(使用java的IO流,而非NIO流)作簡單研究

根據上一篇提到的coyote的介面,這個包主要有以下幾個類:

  • Http11Protocol,實現了ProtocolHandler介面
  • Http11Processor,實現了ActionHook介面
  • InternalInputBuffer,實現了InputBuffer介面
  • InternalOutputBuffer,實現了OutputBuffer介面
  • InputFilter和OutputFilter介面,具體的實現類在 org.apache.coyote.http11.filters 中

下面是這幾個類之間的關係,隨便畫了一幅圖,湊合著看看^_^

coyote http11

大致過程如下:

  1. JIOEndpoint起到一個連線池的作用,可以啟動多個socket監聽,一旦收到瀏覽器發來的請求後,把對應的socket物件通過process方法,傳遞給Http11ConnectionHandler,再交給Http11Processor
  2. Http11Processor內部有個InternalInputBuffer(圖上未畫出),InternalInputBuffer是真正對socket中包含的位元組流進行處理的,它將位元組轉換為Request
  3. Request流經過濾器filters,最後到達實現了Adapter介面的容器,coyote的工作就到此為止,回頭繼續處理下一個socket

下面是幾個主要類的功能介紹

Http11Protocol

http1.1協議的ProtocolHandler實現

主要包含 
Http11ConnectionHandler(內部類) 
JIoEndpoint 
ServerSocketFactory(J2SE)

大致過程如下:

在init方法中,將ServerSocketFactory、Http11ConnectionHandler傳遞給JIoEndpoint進行初始化

然後,在start、pause等方法中,同樣也會呼叫JIoEndpoint的start、pause

JIoEndpoint可以設定最大執行緒數、優先順序、埠等屬性,根據這些屬性,JIoEndpoint生成對應數量的ServerSocketFactory,用於監聽相應的埠,一旦收到http請求,JIoEndpoint則將對應的Socket例項傳遞給Http11ConnectionHandler.process方法進行處理;而Http11ConnectionHandler裡頭會有一個processor例項,這個例項真正處理socket並將其中的資料轉換為Request物件

因此,ProtocolHandler的作用就是把所有這些和連線有關的元件包裝起來,統一設定它們的屬性,並負責控制它們的生命週期

Http11Processor

這個類的作用就是生成Request(當然本質上還是InternalInputBuffer完成的),交給實現了Adapter介面的容器

這個類有adapter、request、response、inputbuffer、outputbuffer等幾個關鍵欄位,其餘就是和http協議有關的欄位了,還有很多方法是關於http協議的,水平有限實在看不懂,估計要先詳細學習一遍http協議才能讀懂,這裡就略過,直接看最關鍵的process(Socket socket) 方法

該方法依次做如下的工作:

  1. 把socket的inputstream和outputstream分別與inputbuffer和outputbuffer關聯起來
  2. 通過inputBuffer.parseRequestLine() 和 inputBuffer.parseHeaders() 方法,解析socket位元組流中的頭欄位,寫到request中
  3. 通過prepareRequest方法組裝filter,用於處理http訊息體
  4. adapter.service(request, response) 把生成的request和response交給容器處理
  5. 如果一切順利,開始處理socket中的下一個請求(因為http1.1是支援持續連線的,所以一個socket中可能包含多個請求),迴圈回到第一步
  6. 如果出錯,則設定response的響應碼,並終止迴圈

prepareRequest方法,用於準備inputbuffer的filter,這裡簡單寫一下。關於filter的機理,請看:

  1. 根據之前對http頭欄位的解析,分別檢查protocol、method、expect、user-agent和MIMEheaders,此外還檢查URI的格式(是否符合:protocol://host:port/ 的格式)
  2. 準備載入filter
  3. 如果有transfer-encoding這個頭欄位(貌似是編碼格式,可以有多個,逗號分割),則分別設定不同編碼的filter
  4. 校驗content-length頭欄位

InternalInputBuffer

研究這個類可以從Http11Processor的process方法入手

這個類的主要功能是:從socket中獲取位元組流,將位元組讀入一個緩衝區buf,然後從緩衝區逐個解析http請求頭以及內容

主要的欄位:

  • request:Request物件,從緩衝區中解析出的資訊會寫入request中
  • buf:緩衝區,從socket的inputstream讀取的位元組放入此緩衝區中
  • headers : MimeHeaders,儲存以鍵值對出現的報頭,也就是除去請求報文第一行之後的所有頭部

具體的http請求報頭的規範,可以參考W3C,或者 
http://www.yuanma.org/data/2008/0827/article_3143.htm

parseRequestLine()

解析請求報頭的第一行,形如:GET http://class/download.microtool.de:80/somedata.exe,包括請求方法(GET or POST)、協議(http)、URI。解析後,放入request中

parseHeader()

解析剛才parseRequestLine()之後的報頭,由於RequestLine之後的報頭都是以“:”分隔的鍵值對,因此每執行一次本方法,則在headers 中加入一個鍵值對,如果格式錯誤則返回false

endRequest()

結束一個request的處理,把多餘的位元組清空

nextRequest()

準備下一個request的處理,這個方法主要用來對所有的標記位和指標進行復位

fill()

從socket的inputstream中讀出一定數量的位元組,填充buf,在很多方法中都有用到。例如解析報頭時,當發現buf已經讀取完了,就呼叫fill重新填充buf,如果inputstream已經讀完了,fill返回false

InternalOutputBuffer的一些疑問

根據inputbuffer的理解,可以大致猜到,這個類是用來從response中讀取資訊,然後寫入socket的outputstream中,返回給客戶端的

類裡面的方法許多也和inputbuffer一樣,但令人納悶的是居然還有nextRequest()、endRequest()方法,而裡面做的事情卻是針對response的(OutputBuffer本來就只有response),看不出任何與request有關的東西。難道是作者拷程式碼過來的時候忘了改方法名稱?

最後,覺得這個類和InternalInputBuffer實在有太多相似之處,為什麼不抽象出一個父類呢?