天天看點

深入Jetty源碼之Servlet架構及實作(ServletRequest、ServletResponse)

servlet是server applet的縮寫,即在伺服器端運作的小程式,而servlet架構則是對http伺服器(servlet container)和使用者小程式中間層的标準化和抽象。這一層抽象隔離了http伺服器的實作細節,而servlet規範定義了各個類的行為,進而保證了這些“伺服器端運作的小程式”對伺服器實作的無關性(即提升了其可移植性)。

在servlet規範有以下幾個核心類(接口):

servletcontext:定義了一些可以和servlet container互動的方法。

registration:實作filter和servlet的動态注冊。

servletrequest(httpservletrequest):對http請求消息的封裝。

servletresponse(httpservletresponse):對http響應消息的封裝。

requestdispatcher:将目前請求分發給另一個url,甚至servletcontext以實作進一步的處理。

servlet(httpservlet):所有“伺服器小程式”要實作了接口,這些“伺服器小程式”重寫doget、dopost、doput、dohead、dodelete、dooption、dotrace等方法(httpservlet)以實作響應請求的相關邏輯。

filter(filterchain):在進入servlet前以及出servlet以後添加一些使用者自定義的邏輯,以實作一些橫切面相關的功能,如使用者驗證、日志列印等功能。

asynccontext:實作異步請求處理。

servletrequest是對servlet請求消息的封裝,其子接口httpservletrequest則是對http請求消息的封裝,在servlet架構中預設實作了servletrequestwrapper和httpservletrequestwrapper以便利使用者對請求的wrap。

深入Jetty源碼之Servlet架構及實作(ServletRequest、ServletResponse)

在jetty中,使用request類實作httpservletrequest接口,request包含了httpconnection引用,因為httpconnection包含了httpparser解析http請求後的所有資訊,如請求行、請求頭以及請求内容。其中servletrequest接口定義與實作如下:

public interface servletrequest {

    // request級别的attribute操作,它屬于request的私有資料,因而request使用attributes私有字段實作。然而jetty添加了一些自定義的attribute:

    // 在getattribute時,org.eclipse.jetty.io.endpoint.maxidletime用于擷取endpoint中maxidletime屬性,org.eclipse.jetty.continuation用于擷取continuation執行個體(如果該屬性沒被占用)

    // 在setattribute時,org.eclipse.jetty.server.request.queryencoding屬性同時用于設定request的queryencoding屬性;

    // org.eclipse.jetty.server.sendcontent同時用于直接向用戶端發送object指定的相應内容,該值必須是httpcontent、resource、buffer、inputstream類型;

    // org.eclipse.jetty.server.responsebuffer同時用于設定對用戶端相應的bytebuffer資料;org.eclipse.jetty.io.endpoint.maxidletime同時用于設定endpoint中的maxidletime屬性。

    // 如果注冊了servletrequestattributelistener,則相應的attributeadded、attributereplaced、attributeremoved方法會被調用。

    public object getattribute(string name);

    public enumeration<string> getattributenames();

    public void setattribute(string name, object o);

    public void removeattribute(string name);

    // characterencoding屬性,用于指定讀取請求内容時使用的編碼方式,如果設定的編碼方式不支援,抛出unsupportedencodingexception。characterencoding的設定必須在讀取parameter或getreader方法的調用之前,不然該方法不會有效果。該屬性預設為null,此時jetty預設使用utf-8編碼parameter,使用iso-8859-1編碼方式建立reader執行個體。

    public string getcharacterencoding();

    public void setcharacterencoding(string env) throws unsupportedencodingexception;

    // 請求内容的長度,以位元組為機關,-1表示長度未知。該值從httpconnection的requestfields字段中擷取,該字段在httpparser解析http請求消息時填充。

    public int getcontentlength();

    // 傳回請求内容的mime type,即在請求頭中設定的contenttype頭,該值同樣從httpconnection的requestfields字段中擷取,該字段在httpparser解析http請求消息時填充。

    public string getcontenttype();

    // 使用servletinputstream包裝請求内容的讀取,該方法和getreader方法,隻能使用一種方式來讀取請求内容,否則抛出illegalstateexception。

    // servletinputstream繼承自inputstream,它隻是實作了一個readline函數。在該方法實作中傳回httpconnection的getinputstream,在該方法傳回前,如果目前請求有expect: 100-continue頭,則先向用戶端發送100 continue相應,然後傳回httpinput執行個體,它繼承自servletinputstream,從httpparser中讀取資料。

    public servletinputstream getinputstream() throws ioexception; 

    // 請求參數相關的操作,請求參數可以在url使用?paramname=paramvalue&...的方式指定,也可是使用post/put方法在請求消息體中指定(contenttype: application/x-www-form-urlencoded),或同時存在。url的parameter資訊讀取使用queryencoding字段指定的編碼方式(預設編碼為utf-8),請求消息體中資訊讀取使用characterencoding字段指定的編碼方式(預設編碼為iso-8859-1)。在讀取請求消息體中的parameter資料時,可以使用contexthandler中的maxformcontentsize屬性或server中的org.eclipse.jetty.server.request.maxformcontentsize的attribute配置最大支援的parameter資料大小,如果contentlength超過該值,則抛出illegalstateexception。

    // 相同的paramname可以多次出現,因而一個paramname可能對應多個值,此時使用getparametervalues()方法擷取所有的值。

    // 如果使用post指定請求參數,使用getinputstream或getreader讀取資料會對parameter的讀取有影響。

    public string getparameter(string name);

    public enumeration<string> getparameternames();

    public string[] getparametervalues(string name);

    public map<string, string[]> getparametermap();

    // 傳回目前請求使用的協定以及其版本号,如http/1.1,對http請求,它在解析請求行時設定。

    public string getprotocol();

    // 傳回請求的schema,預設為http,在ssl相關的connector的customize方法中會被設定為https。

    public string getscheme();

    // 傳回目前請求發送的伺服器名稱,如果請求uri中包含host name資訊,則servername使用該資訊;否則使用host頭(值為host:port)中的server name資訊;

    // 如果不存在host頭,嘗試使用endpont中的localhost(localaddr,如果_dns值設定為false,即connector的resolvenames屬性為false,其預設值為false);

    // 否則使用目前server的localhost的address:inetaddress.getlocalhost().gethostaddress();

    // 對代理伺服器,如果jetty在connector中開啟了forwarded檢查,如果connector中設定了_hostheader值,則使用強制設定host頭為該值,清除已有的servername和port,并重新計算;

    // 否則如果存在x-forwarded-host頭,強制設定host頭為該頭值的最左邊的值,即第一個代理伺服器的主機名,清除已有的servername和port,并重新計算;

    // 如果存在x-forwarded-server頭,則強制設定request的servername值為該頭值的最左邊的值,即第一個代理伺服器的主機名。

    // 如果存在x-forwarded-for頭,則更新request的remoteaddr和remotehost值,即最原始用戶端的位址和主機名。

    // 如果存在x-forwarded-proto頭,則更新request的schema為該頭值的最左邊的值。(這是原始請求的schema還是神馬呢?)

    // 具體參考:http://benni82.iteye.com/blog/849139

    public string getservername();

    // serverport有一下查找路徑:請求uri中的port;host頭中值的port;endpoint的localport;如果都沒有找到,則對https預設值為443,對http預設值為80

    public int getserverport();

    // 将servletinputstream包裹成bufferedreader類型,使用characterencoding編碼方式,如果沒有設定該編碼,預設使用iso-8559-1,因而characterencoding的設定必須在該方法調用之前。

    public bufferedreader getreader() throws ioexception;

    // 傳回用戶端的ip位址,如果開啟了forwarded檢查,則該值會被更新為原始的用戶端請求ip,即使該請求穿越了好幾個代理伺服器。

    public string getremoteaddr();

    // 如果connector設定了resolvenames屬性為true,即request中_dns字段為true,則傳回用戶端主機名,否則傳回用戶端主機ip;如果開啟了forwarded檢查,則該值被更新為最原始的用戶端的主機名或ip,即使該請求穿越了好幾個代理伺服器。

    public string getremotehost();

    // 傳回accept-language請求頭中的指定的locale值,支援多個值,以",", " ", "\t"等字元分隔,每個值都可以是如下格式:language-<country>;q<value>或language-<country>; q=<value>的格式,在選擇一個locale時使用qvalue最大的那個。如果沒有設定,預設使用目前伺服器的locale值。

    public locale getlocale();

    // 傳回accept-language頭中指定的所有locale枚舉值,以qvalue的值降序排列,如果沒有指定該頭,使用伺服器預設的locale值。

    public enumeration<locale> getlocales();

    // 檢查目前請求是否在安全傳輸通道,如https。

    public boolean issecure();

    // 傳回一個requestdispatcher,内部使用servletcontext擷取requestdispatcher執行個體,根據傳入的path計算uriincontext值:如果它以"/"開頭,uriincontext的值即為該值,

    // 如果它不以"/"開頭,即表示它是相對與目前請求的path,則uriincontext的值為相對于目前request uri,如pathinfo為/foo/goo,path為poo,則uriincontext為:<servletpath>/foo/poo

   public requestdispatcher getrequestdispatcher(string path);

    // 傳回用戶端的端口,調用endpoint的getremoteport方法。

    public int getremoteport();

    // 傳回目前伺服器的主機名,如果connector的resolvenames為true,否則為目前伺服器的ip位址。

    public string getlocalname();

    // 傳回目前伺服器的ip位址,調用endpoint的getlocaladdr方法。

    public string getlocaladdr();

    // 傳回目前伺服器的端口号,即目前連接配接使用的端口号,不是伺服器本身監聽的端口号。

    public int getlocalport();

    // 傳回目前request正在執行所在servletcontext。

    public servletcontext getservletcontext();

    // 啟動目前請求的異步模式,該方法調用後,即可以推出目前請求的處理方法,在退出之前需要将傳回的asynccontext放到一個等待queue或類似的資料結構中,進而在之後的進行中還能得到這個

    // asynccontext執行個體進一步處理這個request,asynccontext包含了對目前request和response執行個體的引用,一般喚起這個異步的請求,使用asynccontext的dispatch方法,

    // 進而保證在下一次的處理過程中依然存在filter鍊通道。這個方法和帶servletrequest、servletresponse參數的方法差別在于:

    // 如果servlet a對應為/url/a,在servlet a中調用request.getrequestdispatcher("/url/b"),此時在servlet b中,如果調用request.startasync().dispatch(),此時會dispatch到/url/a,

    // 但是如果在servlet b中調用request.startasync(request, response).dispatch(),此時會dispatch到/url/b中。

    // 另外該方法會在調用之前注冊的asynclistener的onstartasync()方法之後,清除這些已注冊的asynclistener。在onstartasync方法中可以将自己重新注冊到asynccontext中,隻是這個設計好奇怪。。。

    public asynccontext startasync() throws illegalstateexception;

    public asynccontext startasync(servletrequest servletrequest, servletresponse servletresponse)

            throws illegalstateexception;

    // 檢視是否目前request異步模式已經啟動,即已經調用startasync方法,但是還沒有調用asynccontext中的dispatch或oncomplete方法。

    public boolean isasyncstarted();

    // 檢視目前request是否支援異步模式,預設為true,如果filter或servlet不支援異步模式,這在調用對應的dofilter、service之前會把該值設定為false。

    public boolean isasyncsupported();

    // 擷取最近一次調用startasync方法時建立的asynccontext執行個體,如果目前request沒有異步模式還沒有啟動,則抛出illegalstateexception。

    public asynccontext getasynccontext();

    // 擷取目前請求的dispatcher類型。dispatcher type是container用于選區對應的filter鍊。請求最初為dispatcher.request;如果調用requestdispatcher.forward()方法,

    // 則變為dispatchertype.forward;如果調用requestdispatcher.include()方法,則變為dispatchertype.include;如果調用asynccontext.dispatch()方法,則變為

    // dispatchertype.async;最後如果請求被dispatch到error page中,則為dispatchertype.error。

    // dispatchertype在httpconnection中的handlerequest方法中,在調用server.handle()/server.handleasync()方法之前,設定為request、async,根據目前request的asynccontext是否處于初始化狀态,如果是,則為request,否則為async狀态,其他值則在dispatcher中forward或include方法中設定。

    public dispatchertype getdispatchertype();

}

httpservletrequest接口定義如下:

public interface httpservletrequest extends servletrequest {

    // servlet的四種驗證方式(他們在web.xml檔案中的long-config/auth-method中定義):

    // basic使用authentication頭傳輸認證資訊,該資訊以base64的編碼方式編碼,當需要使用者驗證時會彈出登陸視窗。

    // form使用表單的方式認證,使用者名和密碼在j_username和j_password字段中,登陸url為/j_security_check,第一次使用表單方式明文傳輸,之後将認證資訊存放在session中,在session實效之前可以不用在手動登陸。

    // digest也時使用authentication頭傳輸認證資訊,但是它使用加密算法将密碼加密。

    // client_cert則使用用戶端證書的方式傳輸認證資訊。

    // 更詳細的内容參考:http://docs.oracle.com/cd/e19798-01/821-1841/bncas/index.html 

    public static final string basic_auth = "basic";

    public static final string form_auth = "form";

    public static final string client_cert_auth = "client_cert";

    public static final string digest_auth = "digest";

    // 傳回目前請求的認證方法,可以是basic、form、client_cert、digest。如果沒有定義對應servlet的認證配置,則傳回null。在jetty實作中,在servlethandler根據配置資訊以及認證的結果設定請求的authentication執行個體,如果authentication執行個體是authentication.deffered執行個體,則先驗證,并設定驗證後的authentication執行個體,如果authentication執行個體是authentication.user執行個體,則設定傳回其authmethod屬性,否則傳回null。

    public string getauthtype();

    // 傳回目前請求包含的cookie數組,從請求的cookie頭中擷取,如果沒有cookie資訊,則傳回null。在jetty實作中,使用cookiecutter類解析cookie頭。

    // cookie值的格式為:<name>=value; [$path=..; $domain=...; $port=...; $version=..]; <name>=<value>.....

    // 在servlet中,cookie類包含comment(描述資訊)、domain(cookie隻是對指定的domain域可見,預設情況下cookie隻會傳輸給設定這個cookie的server)、maxage(cookie的最長可存活秒數,負數表示永遠不會失效,0表示cookie會被删除)、path(指定那個path下的請求這個cookie才會被發送,包括它的子目錄)、secure(這個cookie是否隻在https請求中發送)、version(0表示netscape最初定義的cookie标準,1表示相容rfc 2109,一般都是0以保持最大相容性)、ishttponly(httponly辨別的cookie一般不應該暴露給用戶端的腳本,以部分解決跨站點腳本攻擊問題)字段。對浏覽器,一般他們應該能為每個web server存儲至少20個cookie,總共至少能處理300個cookie,以及至少4k大小。

    public cookie[] getcookies();

    // 解析指定請求頭的date值,并轉換成long值,如if-modified-since頭。jetty支援的date格式有:eee, dd mmm yyyy hh:mm:ss zzz; eee, dd-mmm-yy hh:mm:ss等。

    public long getdateheader(string name);

    // 傳回指定的請求頭的值,沒有該頭,傳回null,如果有多個相同名字的頭,則傳回第一個該頭的值。name是大小寫無關。

    public string getheader(string name); 

    // 傳回指定請求頭的所有值

    public enumeration<string> getheaders(string name); 

    // 傳回請求頭的所有名稱,有些container會禁用該方法,此時傳回null。

    public enumeration<string> getheadernames();

    // 傳回指定請求頭的int值。

    public int getintheader(string name);

    // 傳回請求方法,如get、post、put等。該值在解析請求行結束後設定。

    public string getmethod();

    // 設定請求的額外path資訊,該值在不同的地方會被設定為不同的值,如果請求的uri為: http://host:80/context/servlet/path/info如在httpconnection中,其值為/context/servlet/path/info

    // 在contexthandler的doscope中,其值被設定為/servlet/path/info;在servlethandler的doscope方法中,其值被設定為/path/info,并設定servletpath為/servlet。

    // 這裡基于的假設為contextpath為/context,servletpath為/servlet,即在web.xml配置中servlet相關的url-pattern為/servlet/*。

    // 如果沒有/path/info字尾,則pathinfo為null,如果servlet的path-pattern設定為/*,則servletpath為"",對url-pattern為*.do類似的配置,pathinfo永遠為null,servletpath則為/path/info.do的值;有些時候servletpath的值也可能是servlet name的值。

   public string getpathinfo();

   public string getservletpath();

    // 傳回pathinfo對應的在servletcontext下的真是server路徑,如果pathinfo為null,則傳回null。

    public string getpathtranslated();

    // 傳回目前請求所屬的contextpath。它在contexthandler的doscope方法中或dispatcher的forward方法中設定。

    public string getcontextpath();

    // 傳回目前請求的query字元串,如果設定了queryencoding,使用該編碼方式編碼。

    public string getquerystring();

    // 傳回請求的登陸使用者,如果使用者沒有認證,則傳回null。該資訊從設定的authentication執行個體中擷取,如果其執行個體為authentication.defered,則需要先驗證,然後傳回擷取useridentity,并從中擷取principal,而從principal中可以擷取使用者名。

    public string getremoteuser();

    // 驗證目前請求的user是否在傳入的role中,即使用設定的authentication執行個體驗證,目前登陸的user所在的roles中是否存在傳入的rolename。其中useridentity.scope執行個體用于查找rolename對應在container使用的rolename,如果沒有這個映射則使用傳入的rolename本身,這個scope在servlethandler的doscope方法中設定。這個映射在web.xml中的security-rol-ref(role-name->role-link)中設定。

    public boolean isuserinrole(string role);

    // 傳回目前登陸user的principal執行個體,從設定的authentication執行個體中擷取,或者為null。

    public java.security.principal getuserprincipal();

    // 傳回用戶端指定的session id,它可以和server的目前session id不同。用戶端可以使用兩種方式設定該值:cookie和url。預設先從cookie中找,找不到再從url中找。

    // 對cookie方式,在web.xml的cookie-config/name中配置session的cookie name(預設值為jssessionid),找到該cookie name對應的cookie值作為requested session id

    // 對url方式,在;<sessionidpathparamname>=....[;#?/]之間的值作為requested session id的值。其中sessionidpathparamname可以通過web.xml的context-param,使用org.eclipse.jetty.servlet.sessionidpathparametername屬性值配置,預設為jsessionid。

    public string getrequestedsessionid();

    public boolean isrequestedsessionidfromcookie();

    public boolean isrequestedsessionidfromurl();

    // 檢查目前requested session id是否valid,在jetty中valid是指requstedsessionid存在,并且和目前請求的server session的clusterid相同,即他們的sessionid相同。

    public boolean isrequestedsessionidvalid();    

    // 傳回/contextpath/servletpath/pathinfo的值。對forward後請求,該值為forward後的uri。

    public string getrequesturi();

    // 傳回getschema()://getservername():getport()/getrequesturi(),對forward後請求,該值為forward後的url。

    public stringbuffer getrequesturl();

    // 傳回和目前請求相關聯的httpsession,如果沒有關聯的httpsession,且create為true,則建立新的httpsession執行個體。沒有參數即create為true。

    public httpsession getsession(boolean create);

    public httpsession getsession();

    //使用container定義的認證機制驗證請求使用者的合法性。在jetty實作中,隻是對authentication.deferred的authentication類型進行驗證,否則傳回401 unauthorized錯誤相應,并傳回false。

    public boolean authenticate(httpservletresponse response)  throws ioexception,servletexception;

    // 提供使用者名和密碼并交由container對其進行認證。在jetty實作中,隻對authentication.deferred類型提供使用者名和密碼認證,否則抛出servletexception。

    public void login(string username, string password)  throws servletexception;

    // 登出目前使用者,并清理_authentication字段。

    public void logout() throws servletexception;

    // 對multipart/form-data請求類型,表示請求内容由多個部分組成,此時使用rfc1867來解析該内容到多個part中。在servlet中一個part有自己的請求頭和請求消息提,包含contenttype、name、headers、size(已寫入的大小)、inputstream等資訊,它還提供了一個方法将請求内容寫入指定的檔案中,以及删除該内部檔案。并提供multipartconfigelement類來做相關的配置,如寫檔案時的位置location;最大可上傳的檔案大小maxfilesize;最大可包含的所有part的請求大小maxrequestsize;如果寫入的資料超過配置的大小,則開始将資料寫入檔案中maxfilesizethreshold。該配置資訊可以使用org.eclipse.multipartconfig屬性設定,而location資訊可以使用javax.servlet.context.tempdir屬性在servletcontext中設定,或這使用java.io.tmpdir中指定的值。在jetty中使用multipartinputstream來表達并解析請求内容到多個part(multipart)中。具體格式可以參考:http://blog.zhaojie.me/2011/03/html-form-file-uploading-programming.html

    public collection<part> getparts() throws ioexception, servletexception;

    public part getpart(string name) throws ioexception, servletexception;

除了以上實作,request還包含了一些額外的字段,如_timestamp在請求開始時設定,即解析請求頭完成時;_dispatchtime在requestloghandler的handle方法,當request是非initial的狀态下設定,即目前request已經為dispatched的狀态;_handled表示該請求是否已經處理完成;

servletresponse是對servlet響應消息的封裝,其子接口httpservletresponse則是對http響應消息的封裝, 

深入Jetty源碼之Servlet架構及實作(ServletRequest、ServletResponse)

servletresponse接口定義如下:

public interface servletresponse {

    // 設定與傳回消息體中的字元編碼方式。如果沒有設定,預設使用iso-8559-1。其中setcontenttype和setlocale會隐式的設定該值,但是顯示的設定會覆寫之前隐式的設定,而該值的設定也會影響contenttype的值。該值的設定必須在調用getwriter()方法之前,否則不會起作用。在set方法中,如果傳入的charset為null,則清楚原來設定的值,并将原來的_mimetype值設定為content-type頭;否則,更新characterencoding的值,并更新contenttype的屬性以及content-type頭(更新contenttype中"charset="之後部分的值,或使用"mimetype; charset=<charactertype>")。

    public void setcharacterencoding(string charset);

    public string getcharacterencoding();

    // 設定響應消息的content-type頭以及contenttype字段的值,它會同時影響mimetype字段以及characterencoding字段。如果type為null,則清除contenttype、mimetype的值,移除content-type響應頭,并且如果locale也為null,同時清除charactertype值;否則,設定mimetype為";"之前的值,而characterencoding的值為"charset="之後的值,如果沒有"charset="之後的值,則是使用"charset=<charactertype>"值拼接以設定content-type響應頭。

    public void setcontenttype(string type);

    public string getcontenttype();

    // 設定locale的值,同時設定響應的content-language頭。同時該set也會影響characterencoding、mimetype和content-type響應頭。其中characterencoding從contexthandler中的locale到charset的映射中擷取,即web.xml中的locale-encoding-mapping-list中定義,而對content-type的值隻影響"charset="之後的值。

    public void setlocale(locale loc);

    // 設定contentlength字段以及content-length響應消息頭。如果目前寫入的資料已經大于或等于設定的值,則該方法還會關閉目前的servletoutputstream或writer。

    public void setcontentlength(int len);  

    // 傳回向用戶端寫資料的servletoutputstream或printwriter。它們隻能使用其中一個方法。在jetty中使用httpoutput内部封裝httpgenerator執行個體用于向socket中寫資料。

    public servletoutputstream getoutputstream() throws ioexception;

    public printwriter getwriter() throws ioexception;    

    // 響應消息處理相關的buffer操作,在jetty中即為httpgenerator中響應消息體的buffer大小與重新整理操作。設定buffersize必須在寫響應消息體之前。另外reset隻是清除消息體的緩存,并不會清除響應狀态碼、響應頭等資訊,如果該方法調用時消息已經被commit,則抛出illegalstateexception。

    public void setbuffersize(int size);

    public int getbuffersize();

    public void flushbuffer() throws ioexception;

    public void resetbuffer();

   // 是否響應消息已經commit,即響應消息狀态行和消息頭已經發送給用戶端。

    public boolean iscommitted();

    // 清除所有的響應消息狀态,所有已經設定的響應頭(但是不包括connection頭),如果響應已經commit,則抛出illegalstateexception。

    public void reset();

httpservletresponse接口定義如下,它添加了一些cookie、header相關的操作:

public interface httpservletresponse extends servletresponse {

    // 向響應消息中添加一個cookie執行個體。即添加"set-cookie"頭。

    public void addcookie(cookie cookie);

    // 消息頭操作:增删改查。一個相同名字的頭可能會有多個條紀錄,因而可以對應多個值。在jetty實作中代理給httpconnection中的responsefields字段。其中date頭的格式為:eee, dd mmm yyyy hh:mm:ss 'gmt',對于string為值的設定來說,處于include dispatch狀态下,隻能設定org.eclipse.jetty.server.include.<headername>的頭。

    public boolean containsheader(string name);

    public void setdateheader(string name, long date);

    public void adddateheader(string name, long date);

    public void setheader(string name, string value);

    public void addheader(string name, string value);

    public void setintheader(string name, int value);

    public void addintheader(string name, int value);

    public collection<string> getheaders(string name); 

    public collection<string> getheadernames();

    // 編碼傳入的url,決定并添加是否需要在url中加入session id資訊以作為session追蹤。處于健壯性的考慮,所有servlet産生的url必須使用改方法編碼,不然對不支援cookie的浏覽器将會失去session資訊。在實作中,如果request使用cookie作為session追蹤,則去除url中的sessionid資訊;否則如果session存在并可用,則向url中添加session追蹤資訊,在"#"或"?"之前。

    public string encodeurl(string url);

    // 編碼傳入的url,用于sendredirect方法中。在jetty中改方法直接調用encodeurl()方法。

    public string encoderedirecturl(string url);

    // 向用戶端發送響應狀态碼和原因(如果有的話)。伺服器會保留已經設定的cookie,但是會在必要情況下修改響應頭。如果在web.xml中定義了響應的狀态碼到error page的映射,則該響應會被轉發到那個錯誤頁面中。在jetty實作中,它清除buffer資訊,characterencoding值,expires、last-modified、cache-control、content-type、content-length頭,設定響應狀态和消息,對非204(no content)、304(not modified)、206(partial content)、200(ok)的狀态碼(即允許有消息體),首先查找有沒有注冊的errorhandler ,如果有,向request的屬性中設定javax.servlet.error.status_code, javax.servlet.error.message, javax.servlet.error.request_uri, javax.servlet.error.servlet_name為相應的值(requestdispatcher中定義的屬性key),并調用errorhandler的handle方法;否則設定cache-control頭為must-revalidate,no-cache,no-store,設定content-type頭為text/html;charset=iso-8859-1,并傳回一個簡單的錯誤頁面包含狀态碼和消息原因。最後調用httpconnection的completeresponse()方法以完成響應。

    public void senderror(int sc, string msg) throws ioexception;

    public void senderror(int sc) throws ioexception;

    // 發送用戶端一個重定向的消息和url,即設者響應狀态碼為302 moved temporary,在location中包含要重定向目的地的url,此時用戶端會使用新的url重新發送請求。如果location以"/"開頭,表示它是絕對位址,否則為相應請求的相對位址,即最終解析成request.getrequesturi()/location,location可以包含query資訊。最後調用httpconnection的completeresponse()方法以完成目前響應。

    public void sendredirect(string location) throws ioexception;

    // 設定響應狀态碼和狀态描述資訊。

    public void setstatus(int sc);

    public void setstatus(int sc, string sm);

    public int getstatus();

繼續閱讀