概述
Servlet是Server Applet的縮寫,即在服務(wù)器端運(yùn)行的小程序,而Servlet框架則是對(duì)HTTP服務(wù)器(Servlet Container)和用戶小程序中間層的標(biāo)準(zhǔn)化和抽象。這一層抽象隔離了HTTP服務(wù)器的實(shí)現(xiàn)細(xì)節(jié),而Servlet規(guī)范定義了各個(gè)類的行為,從而保證了這些“服務(wù)器端運(yùn)行的小程序”對(duì)服務(wù)器實(shí)現(xiàn)的無(wú)關(guān)性(即提升了其可移植性)。在Servlet規(guī)范有以下幾個(gè)核心類(接口):
ServletContext:定義了一些可以和Servlet Container交互的方法。
Registration:實(shí)現(xiàn)Filter和Servlet的動(dòng)態(tài)注冊(cè)。
ServletRequest(HttpServletRequest):對(duì)HTTP請(qǐng)求消息的封裝。
ServletResponse(HttpServletResponse):對(duì)HTTP響應(yīng)消息的封裝。
RequestDispatcher:將當(dāng)前請(qǐng)求分發(fā)給另一個(gè)URL,甚至ServletContext以實(shí)現(xiàn)進(jìn)一步的處理。
Servlet(HttpServlet):所有“服務(wù)器小程序”要實(shí)現(xiàn)了接口,這些“服務(wù)器小程序”重寫doGet、doPost、doPut、doHead、doDelete、doOption、doTrace等方法(HttpServlet)以實(shí)現(xiàn)響應(yīng)請(qǐng)求的相關(guān)邏輯。
Filter(FilterChain):在進(jìn)入Servlet前以及出Servlet以后添加一些用戶自定義的邏輯,以實(shí)現(xiàn)一些橫切面相關(guān)的功能,如用戶驗(yàn)證、日志打印等功能。
AsyncContext:實(shí)現(xiàn)異步請(qǐng)求處理。
ServletRequest
ServletRequest是對(duì)Servlet請(qǐng)求消息的封裝,其子接口HttpServletRequest則是對(duì)HTTP請(qǐng)求消息的封裝,在Servlet框架中默認(rèn)實(shí)現(xiàn)了ServletRequestWrapper和HttpServletRequestWrapper以便利用戶對(duì)請(qǐng)求的Wrap。
在Jetty中,使用Request類實(shí)現(xiàn)HttpServletRequest接口,Request包含了HttpConnection引用,因?yàn)镠ttpConnection包含了HttpParser解析HTTP請(qǐng)求后的所有信息,如請(qǐng)求行、請(qǐng)求頭以及請(qǐng)求內(nèi)容。其中ServletRequest接口定義與實(shí)現(xiàn)如下:
public interface ServletRequest {
// Request級(jí)別的Attribute操作,它屬于Request的私有數(shù)據(jù),因而Request使用Attributes私有字段實(shí)現(xiàn)。然而Jetty添加了一些自定義的Attribute:
// 在getAttribute時(shí),org.eclipse.jetty.io.EndPoint.maxIdleTime用于獲取EndPoint中MaxIdleTime屬性,org.eclipse.jetty.continuation用于獲取Continuation實(shí)例(如果該屬性沒(méi)被占用)
// 在setAttribute時(shí),org.eclipse.jetty.server.Request.queryEncoding屬性同時(shí)用于設(shè)置Request的QueryEncoding屬性;
// org.eclipse.jetty.server.sendContent同時(shí)用于直接向客戶端發(fā)送Object指定的相應(yīng)內(nèi)容,該值必須是HttpContent、Resource、Buffer、InputStream類型;
// org.eclipse.jetty.server.ResponseBuffer同時(shí)用于設(shè)置對(duì)客戶端相應(yīng)的ByteBuffer數(shù)據(jù);org.eclipse.jetty.io.EndPoint.maxIdleTime同時(shí)用于設(shè)置EndPoint中的MaxIdleTime屬性。
// 如果注冊(cè)了ServletRequestAttributeListener,則相應(yīng)的attributeAdded、attributeReplaced、attributeRemoved方法會(huì)被調(diào)用。
public Object getAttribute(String name);
public Enumeration<String> getAttributeNames();
public void setAttribute(String name, Object o);
public void removeAttribute(String name);
// CharacterEncoding屬性,用于指定讀取請(qǐng)求內(nèi)容時(shí)使用的編碼方式,如果設(shè)置的編碼方式不支持,拋出UnsupportedEncodingException。CharacterEncoding的設(shè)置必須在讀取Parameter或getReader方法的調(diào)用之前,不然該方法不會(huì)有效果。該屬性默認(rèn)為null,此時(shí)Jetty默認(rèn)使用UTF-8編碼Parameter,使用ISO-8859-1編碼方式創(chuàng)建Reader實(shí)例。
public String getCharacterEncoding();
public void setCharacterEncoding(String env) throws UnsupportedEncodingException;
// 請(qǐng)求內(nèi)容的長(zhǎng)度,以字節(jié)為單位,-1表示長(zhǎng)度未知。該值從HttpConnection的RequestFields字段中獲取,該字段在HttpParser解析HTTP請(qǐng)求消息時(shí)填充。
public int getContentLength();
// 返回請(qǐng)求內(nèi)容的MIME type,即在請(qǐng)求頭中設(shè)置的ContentType頭,該值同樣從HttpConnection的RequestFields字段中獲取,該字段在HttpParser解析HTTP請(qǐng)求消息時(shí)填充。
public String getContentType();
// 使用ServletInputStream包裝請(qǐng)求內(nèi)容的讀取,該方法和getReader方法,只能使用一種方式來(lái)讀取請(qǐng)求內(nèi)容,否則拋出IllegalStateException。
// ServletInputStream繼承自InputStream,它只是實(shí)現(xiàn)了一個(gè)readLine函數(shù)。在該方法實(shí)現(xiàn)中返回HttpConnection的getInputStream,在該方法返回前,如果當(dāng)前請(qǐng)求有Expect: 100-continue頭,則先向客戶端發(fā)送100 Continue相應(yīng),然后返回HttpInput實(shí)例,它繼承自ServletInputStream,從HttpParser中讀取數(shù)據(jù)。
public ServletInputStream getInputStream() throws IOException;
// 請(qǐng)求參數(shù)相關(guān)的操作,請(qǐng)求參數(shù)可以在URL使用?paramName=paramValue&...的方式指定,也可是使用POST/PUT方法在請(qǐng)求消息體中指定(ContentType: application/x-www-form-urlencoded),或同時(shí)存在。URL的parameter信息讀取使用queryEncoding字段指定的編碼方式(默認(rèn)編碼為UTF-8),請(qǐng)求消息體中信息讀取使用characterEncoding字段指定的編碼方式(默認(rèn)編碼為ISO-8859-1)。在讀取請(qǐng)求消息體中的parameter數(shù)據(jù)時(shí),可以使用ContextHandler中的MaxFormContentSize屬性或Server中的org.eclipse.jetty.server.Request.maxFormContentSize的Attribute配置最大支持的parameter數(shù)據(jù)大小,如果ContentLength超過(guò)該值,則拋出IllegalStateException。
// 相同的paramName可以多次出現(xiàn),因而一個(gè)paramName可能對(duì)應(yīng)多個(gè)值,此時(shí)使用getParameterValues()方法獲取所有的值。
// 如果使用POST指定請(qǐng)求參數(shù),使用getInputStream或getReader讀取數(shù)據(jù)會(huì)對(duì)parameter的讀取有影響。
public String getParameter(String name);
public Enumeration<String> getParameterNames();
public String[] getParameterValues(String name);
public Map<String, String[]> getParameterMap();
// 返回當(dāng)前請(qǐng)求使用的協(xié)議以及其版本號(hào),如HTTP/1.1,對(duì)HTTP請(qǐng)求,它在解析請(qǐng)求行時(shí)設(shè)置。
public String getProtocol();
// 返回請(qǐng)求的Schema,默認(rèn)為http,在SSL相關(guān)的Connector的customize方法中會(huì)被設(shè)置為https。
public String getScheme();
// 返回當(dāng)前請(qǐng)求發(fā)送的服務(wù)器名稱,如果請(qǐng)求URI中包含Host Name信息,則ServerName使用該信息;否則使用Host頭(值為host:port)中的Server Name信息;
// 如果不存在Host頭,嘗試使用EndPont中的LocalHost(LocalAddr,如果_dns值設(shè)置為false,即Connector的ResolveNames屬性為false,其默認(rèn)值為false);
// 否則使用當(dāng)前Server的LocalHost的Address:InetAddress.getLocalHost().getHostAddress();
// 對(duì)代理服務(wù)器,如果Jetty在Connector中開(kāi)啟了forwarded檢查,如果Connector中設(shè)置了_hostHeader值,則使用強(qiáng)制設(shè)置Host頭為該值,清除已有的ServerName和Port,并重新計(jì)算;
// 否則如果存在X-Forwarded-Host頭,強(qiáng)制設(shè)置Host頭為該頭值的最左邊的值,即第一個(gè)代理服務(wù)器的主機(jī)名,清除已有的ServerName和Port,并重新計(jì)算;
// 如果存在X-Forwarded-Server頭,則強(qiáng)制設(shè)置Request的ServerName值為該頭值的最左邊的值,即第一個(gè)代理服務(wù)器的主機(jī)名。
// 如果存在X-Forwarded-For頭,則更新Request的RemoteAddr和RemoteHost值,即最原始客戶端的地址和主機(jī)名。
// 如果存在X-Forwarded-Proto頭,則更新Request的Schema為該頭值的最左邊的值。(這是原始請(qǐng)求的Schema還是神馬呢?)
// 具體參考:http://benni82.iteye.com/blog/849139
public String getServerName();
// ServerPort有一下查找路徑:請(qǐng)求URI中的Port;Host頭中值的Port;EndPoint的LocalPort;如果都沒(méi)有找到,則對(duì)HTTPS默認(rèn)值為443,對(duì)HTTP默認(rèn)值為80
public int getServerPort();
// 將ServletInputStream包裹成BufferedReader類型,使用CharacterEncoding編碼方式,如果沒(méi)有設(shè)置該編碼,默認(rèn)使用ISO-8559-1,因而CharacterEncoding的設(shè)置必須在該方法調(diào)用之前。
public BufferedReader getReader() throws IOException;
// 返回客戶端的IP地址,如果開(kāi)啟了forwarded檢查,則該值會(huì)被更新為原始的客戶端請(qǐng)求IP,即使該請(qǐng)求穿越了好幾個(gè)代理服務(wù)器。
public String getRemoteAddr();
// 如果Connector設(shè)置了ResolveNames屬性為true,即Request中_dns字段為true,則返回客戶端主機(jī)名,否則返回客戶端主機(jī)IP;如果開(kāi)啟了forwarded檢查,則該值被更新為最原始的客戶端的主機(jī)名或IP,即使該請(qǐng)求穿越了好幾個(gè)代理服務(wù)器。
public String getRemoteHost();
// 返回Accept-Language請(qǐng)求頭中的指定的Locale值,支持多個(gè)值,以",", " ", "\t"等字符分隔,每個(gè)值都可以是如下格式:language-<country>;q<value>或language-<country>; q=<value>的格式,在選擇一個(gè)Locale時(shí)使用qValue最大的那個(gè)。如果沒(méi)有設(shè)置,默認(rèn)使用當(dāng)前服務(wù)器的Locale值。
public Locale getLocale();
// 返回Accept-Language頭中指定的所有Locale枚舉值,以qValue的值降序排列,如果沒(méi)有指定該頭,使用服務(wù)器默認(rèn)的Locale值。
public Enumeration<Locale> getLocales();
// 檢查當(dāng)前請(qǐng)求是否在安全傳輸通道,如https。
public boolean isSecure();
// 返回一個(gè)RequestDispatcher,內(nèi)部使用ServletContext獲取RequestDispatcher實(shí)例,根據(jù)傳入的path計(jì)算uriInContext值:如果它以"/"開(kāi)頭,uriInContext的值即為該值,
// 如果它不以"/"開(kāi)頭,即表示它是相對(duì)與當(dāng)前請(qǐng)求的path,則uriInContext的值為相對(duì)于當(dāng)前Request URI,如pathInfo為/foo/goo,path為poo,則uriInContext為:<servletPath>/foo/poo
public RequestDispatcher getRequestDispatcher(String path);
// 返回客戶端的端口,調(diào)用EndPoint的getRemotePort方法。
public int getRemotePort();
// 返回當(dāng)前服務(wù)器的主機(jī)名,如果Connector的ResolveNames為true,否則為當(dāng)前服務(wù)器的IP地址。
public String getLocalName();
// 返回當(dāng)前服務(wù)器的IP地址,調(diào)用EndPoint的getLocalAddr方法。
public String getLocalAddr();
// 返回當(dāng)前服務(wù)器的端口號(hào),即當(dāng)前連接使用的端口號(hào),不是服務(wù)器本身監(jiān)聽(tīng)的端口號(hào)。
public int getLocalPort();
// 返回當(dāng)前Request正在執(zhí)行所在ServletContext。
public ServletContext getServletContext();
// 啟動(dòng)當(dāng)前請(qǐng)求的異步模式,該方法調(diào)用后,即可以推出當(dāng)前請(qǐng)求的處理方法,在退出之前需要將返回的AsyncContext放到一個(gè)等待Queue或類似的數(shù)據(jù)結(jié)構(gòu)中,從而在之后的處理中還能得到這個(gè)
// AsyncContext實(shí)例進(jìn)一步處理這個(gè)Request,AsyncContext包含了對(duì)當(dāng)前Request和Response實(shí)例的引用,一般喚起這個(gè)異步的請(qǐng)求,使用AsyncContext的dispatch方法,
// 從而保證在下一次的處理過(guò)程中依然存在Filter鏈通道。這個(gè)方法和帶ServletRequest、ServletResponse參數(shù)的方法區(qū)別在于:
// 如果Servlet A對(duì)應(yīng)為/url/A,在Servlet A中調(diào)用request.getRequestDispatcher("/url/B"),此時(shí)在Servlet B中,如果調(diào)用request.startAsync().dispatch(),此時(shí)會(huì)dispatch到/url/A,
// 但是如果在Servlet B中調(diào)用request.startAsync(request, response).dispatch(),此時(shí)會(huì)dispatch到/url/B中。
// 另外該方法會(huì)在調(diào)用之前注冊(cè)的AsyncListener的onStartAsync()方法之后,清除這些已注冊(cè)的AsyncListener。在onStartAsync方法中可以將自己重新注冊(cè)到AsyncContext中,只是這個(gè)設(shè)計(jì)好奇怪。。。
public AsyncContext startAsync() throws IllegalStateException;
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
throws IllegalStateException;
// 查看是否當(dāng)前request異步模式已經(jīng)啟動(dòng),即已經(jīng)調(diào)用startAsync方法,但是還沒(méi)有調(diào)用AsyncContext中的dispatch或onComplete方法。
public boolean isAsyncStarted();
// 查看當(dāng)前Request是否支持異步模式,默認(rèn)為true,如果Filter或Servlet不支持異步模式,這在調(diào)用對(duì)應(yīng)的doFilter、service之前會(huì)把該值設(shè)置為false。
public boolean isAsyncSupported();
// 獲取最近一次調(diào)用startAsync方法時(shí)創(chuàng)建的AsyncContext實(shí)例,如果當(dāng)前request沒(méi)有異步模式還沒(méi)有啟動(dòng),則拋出IllegalStateException。
public AsyncContext getAsyncContext();
// 獲取當(dāng)前請(qǐng)求的Dispatcher類型。Dispatcher Type是Container用于選區(qū)對(duì)應(yīng)的Filter鏈。請(qǐng)求最初為Dispatcher.REQUEST;如果調(diào)用RequestDispatcher.forward()方法,
// 則變?yōu)镈ispatcherType.FORWARD;如果調(diào)用RequestDispatcher.include()方法,則變?yōu)镈ispatcherType.INCLUDE;如果調(diào)用AsyncContext.dispatch()方法,則變?yōu)?br /> // DispatcherType.ASYNC;最后如果請(qǐng)求被dispatch到error page中,則為DispatcherType.ERROR。
// DispatcherType在HttpConnection中的handleRequest方法中,在調(diào)用server.handle()/server.handleAsync()方法之前,設(shè)置為REQUEST、ASYNC,根據(jù)當(dāng)前Request的AsyncContext是否處于初始化狀態(tài),如果是,則為REQUEST,否則為ASYNC狀態(tài),其他值則在Dispatcher中forward或include方法中設(shè)置。
public DispatcherType getDispatcherType();
}
HttpServletRequest接口定義如下:// Request級(jí)別的Attribute操作,它屬于Request的私有數(shù)據(jù),因而Request使用Attributes私有字段實(shí)現(xiàn)。然而Jetty添加了一些自定義的Attribute:
// 在getAttribute時(shí),org.eclipse.jetty.io.EndPoint.maxIdleTime用于獲取EndPoint中MaxIdleTime屬性,org.eclipse.jetty.continuation用于獲取Continuation實(shí)例(如果該屬性沒(méi)被占用)
// 在setAttribute時(shí),org.eclipse.jetty.server.Request.queryEncoding屬性同時(shí)用于設(shè)置Request的QueryEncoding屬性;
// org.eclipse.jetty.server.sendContent同時(shí)用于直接向客戶端發(fā)送Object指定的相應(yīng)內(nèi)容,該值必須是HttpContent、Resource、Buffer、InputStream類型;
// org.eclipse.jetty.server.ResponseBuffer同時(shí)用于設(shè)置對(duì)客戶端相應(yīng)的ByteBuffer數(shù)據(jù);org.eclipse.jetty.io.EndPoint.maxIdleTime同時(shí)用于設(shè)置EndPoint中的MaxIdleTime屬性。
// 如果注冊(cè)了ServletRequestAttributeListener,則相應(yīng)的attributeAdded、attributeReplaced、attributeRemoved方法會(huì)被調(diào)用。
public Object getAttribute(String name);
public Enumeration<String> getAttributeNames();
public void setAttribute(String name, Object o);
public void removeAttribute(String name);
// CharacterEncoding屬性,用于指定讀取請(qǐng)求內(nèi)容時(shí)使用的編碼方式,如果設(shè)置的編碼方式不支持,拋出UnsupportedEncodingException。CharacterEncoding的設(shè)置必須在讀取Parameter或getReader方法的調(diào)用之前,不然該方法不會(huì)有效果。該屬性默認(rèn)為null,此時(shí)Jetty默認(rèn)使用UTF-8編碼Parameter,使用ISO-8859-1編碼方式創(chuàng)建Reader實(shí)例。
public String getCharacterEncoding();
public void setCharacterEncoding(String env) throws UnsupportedEncodingException;
// 請(qǐng)求內(nèi)容的長(zhǎng)度,以字節(jié)為單位,-1表示長(zhǎng)度未知。該值從HttpConnection的RequestFields字段中獲取,該字段在HttpParser解析HTTP請(qǐng)求消息時(shí)填充。
public int getContentLength();
// 返回請(qǐng)求內(nèi)容的MIME type,即在請(qǐng)求頭中設(shè)置的ContentType頭,該值同樣從HttpConnection的RequestFields字段中獲取,該字段在HttpParser解析HTTP請(qǐng)求消息時(shí)填充。
public String getContentType();
// 使用ServletInputStream包裝請(qǐng)求內(nèi)容的讀取,該方法和getReader方法,只能使用一種方式來(lái)讀取請(qǐng)求內(nèi)容,否則拋出IllegalStateException。
// ServletInputStream繼承自InputStream,它只是實(shí)現(xiàn)了一個(gè)readLine函數(shù)。在該方法實(shí)現(xiàn)中返回HttpConnection的getInputStream,在該方法返回前,如果當(dāng)前請(qǐng)求有Expect: 100-continue頭,則先向客戶端發(fā)送100 Continue相應(yīng),然后返回HttpInput實(shí)例,它繼承自ServletInputStream,從HttpParser中讀取數(shù)據(jù)。
public ServletInputStream getInputStream() throws IOException;
// 請(qǐng)求參數(shù)相關(guān)的操作,請(qǐng)求參數(shù)可以在URL使用?paramName=paramValue&...的方式指定,也可是使用POST/PUT方法在請(qǐng)求消息體中指定(ContentType: application/x-www-form-urlencoded),或同時(shí)存在。URL的parameter信息讀取使用queryEncoding字段指定的編碼方式(默認(rèn)編碼為UTF-8),請(qǐng)求消息體中信息讀取使用characterEncoding字段指定的編碼方式(默認(rèn)編碼為ISO-8859-1)。在讀取請(qǐng)求消息體中的parameter數(shù)據(jù)時(shí),可以使用ContextHandler中的MaxFormContentSize屬性或Server中的org.eclipse.jetty.server.Request.maxFormContentSize的Attribute配置最大支持的parameter數(shù)據(jù)大小,如果ContentLength超過(guò)該值,則拋出IllegalStateException。
// 相同的paramName可以多次出現(xiàn),因而一個(gè)paramName可能對(duì)應(yīng)多個(gè)值,此時(shí)使用getParameterValues()方法獲取所有的值。
// 如果使用POST指定請(qǐng)求參數(shù),使用getInputStream或getReader讀取數(shù)據(jù)會(huì)對(duì)parameter的讀取有影響。
public String getParameter(String name);
public Enumeration<String> getParameterNames();
public String[] getParameterValues(String name);
public Map<String, String[]> getParameterMap();
// 返回當(dāng)前請(qǐng)求使用的協(xié)議以及其版本號(hào),如HTTP/1.1,對(duì)HTTP請(qǐng)求,它在解析請(qǐng)求行時(shí)設(shè)置。
public String getProtocol();
// 返回請(qǐng)求的Schema,默認(rèn)為http,在SSL相關(guān)的Connector的customize方法中會(huì)被設(shè)置為https。
public String getScheme();
// 返回當(dāng)前請(qǐng)求發(fā)送的服務(wù)器名稱,如果請(qǐng)求URI中包含Host Name信息,則ServerName使用該信息;否則使用Host頭(值為host:port)中的Server Name信息;
// 如果不存在Host頭,嘗試使用EndPont中的LocalHost(LocalAddr,如果_dns值設(shè)置為false,即Connector的ResolveNames屬性為false,其默認(rèn)值為false);
// 否則使用當(dāng)前Server的LocalHost的Address:InetAddress.getLocalHost().getHostAddress();
// 對(duì)代理服務(wù)器,如果Jetty在Connector中開(kāi)啟了forwarded檢查,如果Connector中設(shè)置了_hostHeader值,則使用強(qiáng)制設(shè)置Host頭為該值,清除已有的ServerName和Port,并重新計(jì)算;
// 否則如果存在X-Forwarded-Host頭,強(qiáng)制設(shè)置Host頭為該頭值的最左邊的值,即第一個(gè)代理服務(wù)器的主機(jī)名,清除已有的ServerName和Port,并重新計(jì)算;
// 如果存在X-Forwarded-Server頭,則強(qiáng)制設(shè)置Request的ServerName值為該頭值的最左邊的值,即第一個(gè)代理服務(wù)器的主機(jī)名。
// 如果存在X-Forwarded-For頭,則更新Request的RemoteAddr和RemoteHost值,即最原始客戶端的地址和主機(jī)名。
// 如果存在X-Forwarded-Proto頭,則更新Request的Schema為該頭值的最左邊的值。(這是原始請(qǐng)求的Schema還是神馬呢?)
// 具體參考:http://benni82.iteye.com/blog/849139
public String getServerName();
// ServerPort有一下查找路徑:請(qǐng)求URI中的Port;Host頭中值的Port;EndPoint的LocalPort;如果都沒(méi)有找到,則對(duì)HTTPS默認(rèn)值為443,對(duì)HTTP默認(rèn)值為80
public int getServerPort();
// 將ServletInputStream包裹成BufferedReader類型,使用CharacterEncoding編碼方式,如果沒(méi)有設(shè)置該編碼,默認(rèn)使用ISO-8559-1,因而CharacterEncoding的設(shè)置必須在該方法調(diào)用之前。
public BufferedReader getReader() throws IOException;
// 返回客戶端的IP地址,如果開(kāi)啟了forwarded檢查,則該值會(huì)被更新為原始的客戶端請(qǐng)求IP,即使該請(qǐng)求穿越了好幾個(gè)代理服務(wù)器。
public String getRemoteAddr();
// 如果Connector設(shè)置了ResolveNames屬性為true,即Request中_dns字段為true,則返回客戶端主機(jī)名,否則返回客戶端主機(jī)IP;如果開(kāi)啟了forwarded檢查,則該值被更新為最原始的客戶端的主機(jī)名或IP,即使該請(qǐng)求穿越了好幾個(gè)代理服務(wù)器。
public String getRemoteHost();
// 返回Accept-Language請(qǐng)求頭中的指定的Locale值,支持多個(gè)值,以",", " ", "\t"等字符分隔,每個(gè)值都可以是如下格式:language-<country>;q<value>或language-<country>; q=<value>的格式,在選擇一個(gè)Locale時(shí)使用qValue最大的那個(gè)。如果沒(méi)有設(shè)置,默認(rèn)使用當(dāng)前服務(wù)器的Locale值。
public Locale getLocale();
// 返回Accept-Language頭中指定的所有Locale枚舉值,以qValue的值降序排列,如果沒(méi)有指定該頭,使用服務(wù)器默認(rèn)的Locale值。
public Enumeration<Locale> getLocales();
// 檢查當(dāng)前請(qǐng)求是否在安全傳輸通道,如https。
public boolean isSecure();
// 返回一個(gè)RequestDispatcher,內(nèi)部使用ServletContext獲取RequestDispatcher實(shí)例,根據(jù)傳入的path計(jì)算uriInContext值:如果它以"/"開(kāi)頭,uriInContext的值即為該值,
// 如果它不以"/"開(kāi)頭,即表示它是相對(duì)與當(dāng)前請(qǐng)求的path,則uriInContext的值為相對(duì)于當(dāng)前Request URI,如pathInfo為/foo/goo,path為poo,則uriInContext為:<servletPath>/foo/poo
public RequestDispatcher getRequestDispatcher(String path);
// 返回客戶端的端口,調(diào)用EndPoint的getRemotePort方法。
public int getRemotePort();
// 返回當(dāng)前服務(wù)器的主機(jī)名,如果Connector的ResolveNames為true,否則為當(dāng)前服務(wù)器的IP地址。
public String getLocalName();
// 返回當(dāng)前服務(wù)器的IP地址,調(diào)用EndPoint的getLocalAddr方法。
public String getLocalAddr();
// 返回當(dāng)前服務(wù)器的端口號(hào),即當(dāng)前連接使用的端口號(hào),不是服務(wù)器本身監(jiān)聽(tīng)的端口號(hào)。
public int getLocalPort();
// 返回當(dāng)前Request正在執(zhí)行所在ServletContext。
public ServletContext getServletContext();
// 啟動(dòng)當(dāng)前請(qǐng)求的異步模式,該方法調(diào)用后,即可以推出當(dāng)前請(qǐng)求的處理方法,在退出之前需要將返回的AsyncContext放到一個(gè)等待Queue或類似的數(shù)據(jù)結(jié)構(gòu)中,從而在之后的處理中還能得到這個(gè)
// AsyncContext實(shí)例進(jìn)一步處理這個(gè)Request,AsyncContext包含了對(duì)當(dāng)前Request和Response實(shí)例的引用,一般喚起這個(gè)異步的請(qǐng)求,使用AsyncContext的dispatch方法,
// 從而保證在下一次的處理過(guò)程中依然存在Filter鏈通道。這個(gè)方法和帶ServletRequest、ServletResponse參數(shù)的方法區(qū)別在于:
// 如果Servlet A對(duì)應(yīng)為/url/A,在Servlet A中調(diào)用request.getRequestDispatcher("/url/B"),此時(shí)在Servlet B中,如果調(diào)用request.startAsync().dispatch(),此時(shí)會(huì)dispatch到/url/A,
// 但是如果在Servlet B中調(diào)用request.startAsync(request, response).dispatch(),此時(shí)會(huì)dispatch到/url/B中。
// 另外該方法會(huì)在調(diào)用之前注冊(cè)的AsyncListener的onStartAsync()方法之后,清除這些已注冊(cè)的AsyncListener。在onStartAsync方法中可以將自己重新注冊(cè)到AsyncContext中,只是這個(gè)設(shè)計(jì)好奇怪。。。
public AsyncContext startAsync() throws IllegalStateException;
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
throws IllegalStateException;
// 查看是否當(dāng)前request異步模式已經(jīng)啟動(dòng),即已經(jīng)調(diào)用startAsync方法,但是還沒(méi)有調(diào)用AsyncContext中的dispatch或onComplete方法。
public boolean isAsyncStarted();
// 查看當(dāng)前Request是否支持異步模式,默認(rèn)為true,如果Filter或Servlet不支持異步模式,這在調(diào)用對(duì)應(yīng)的doFilter、service之前會(huì)把該值設(shè)置為false。
public boolean isAsyncSupported();
// 獲取最近一次調(diào)用startAsync方法時(shí)創(chuàng)建的AsyncContext實(shí)例,如果當(dāng)前request沒(méi)有異步模式還沒(méi)有啟動(dòng),則拋出IllegalStateException。
public AsyncContext getAsyncContext();
// 獲取當(dāng)前請(qǐng)求的Dispatcher類型。Dispatcher Type是Container用于選區(qū)對(duì)應(yīng)的Filter鏈。請(qǐng)求最初為Dispatcher.REQUEST;如果調(diào)用RequestDispatcher.forward()方法,
// 則變?yōu)镈ispatcherType.FORWARD;如果調(diào)用RequestDispatcher.include()方法,則變?yōu)镈ispatcherType.INCLUDE;如果調(diào)用AsyncContext.dispatch()方法,則變?yōu)?br /> // DispatcherType.ASYNC;最后如果請(qǐng)求被dispatch到error page中,則為DispatcherType.ERROR。
// DispatcherType在HttpConnection中的handleRequest方法中,在調(diào)用server.handle()/server.handleAsync()方法之前,設(shè)置為REQUEST、ASYNC,根據(jù)當(dāng)前Request的AsyncContext是否處于初始化狀態(tài),如果是,則為REQUEST,否則為ASYNC狀態(tài),其他值則在Dispatcher中forward或include方法中設(shè)置。
public DispatcherType getDispatcherType();
}
public interface HttpServletRequest extends ServletRequest {
// Servlet的四種驗(yàn)證方式(他們?cè)趙eb.xml文件中的long-config/auth-method中定義):
// BASIC使用Authentication頭傳輸認(rèn)證信息,該信息以Base64的編碼方式編碼,當(dāng)需要用戶驗(yàn)證時(shí)會(huì)彈出登陸窗口。
// FORM使用表單的方式認(rèn)證,用戶名和密碼在j_username和j_password字段中,登陸URL為/j_security_check,第一次使用表單方式明文傳輸,之后將認(rèn)證信息存放在Session中,在Session實(shí)效之前可以不用在手動(dòng)登陸。
// DIGEST也時(shí)使用Authentication頭傳輸認(rèn)證信息,但是它使用加密算法將密碼加密。
// CLIENT_CERT則使用客戶端證書的方式傳輸認(rèn)證信息。
// 更詳細(xì)的內(nèi)容參考: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";
// 返回當(dāng)前請(qǐng)求的認(rèn)證方法,可以是BASIC、FORM、CLIENT_CERT、DIGEST。如果沒(méi)有定義對(duì)應(yīng)Servlet的認(rèn)證配置,則返回null。在Jetty實(shí)現(xiàn)中,在ServletHandler根據(jù)配置信息以及認(rèn)證的結(jié)果設(shè)置請(qǐng)求的Authentication實(shí)例,如果Authentication實(shí)例是Authentication.Deffered實(shí)例,則先驗(yàn)證,并設(shè)置驗(yàn)證后的Authentication實(shí)例,如果Authentication實(shí)例是Authentication.User實(shí)例,則設(shè)置返回其AuthMethod屬性,否則返回null。
public String getAuthType();
// 返回當(dāng)前請(qǐng)求包含的Cookie數(shù)組,從請(qǐng)求的Cookie頭中獲取,如果沒(méi)有Cookie信息,則返回null。在Jetty實(shí)現(xiàn)中,使用CookieCutter類解析Cookie頭。
// Cookie值的格式為:<name>=value; [$path=..; $domain=...; $port=...; $version=..]; <name>=<value>.....
// 在Servlet中,Cookie類包含comment(描述信息)、domain(cookie只是對(duì)指定的domain域可見(jiàn),默認(rèn)情況下cookie只會(huì)傳輸給設(shè)置這個(gè)cookie的server)、maxAge(cookie的最長(zhǎng)可存活秒數(shù),負(fù)數(shù)表示永遠(yuǎn)不會(huì)失效,0表示cookie會(huì)被刪除)、path(指定那個(gè)path下的請(qǐng)求這個(gè)cookie才會(huì)被發(fā)送,包括它的子目錄)、secure(這個(gè)cookie是否只在https請(qǐng)求中發(fā)送)、version(0表示netscape最初定義的cookie標(biāo)準(zhǔn),1表示兼容RFC 2109,一般都是0以保持最大兼容性)、isHttpOnly(HttpOnly標(biāo)識(shí)的cookie一般不應(yīng)該暴露給客戶端的腳本,以部分解決跨站點(diǎn)腳本攻擊問(wèn)題)字段。對(duì)瀏覽器,一般他們應(yīng)該能為每個(gè)Web Server存儲(chǔ)至少20個(gè)cookie,總共至少能處理300個(gè)cookie,以及至少4k大小。
public Cookie[] getCookies();
// 解析指定請(qǐng)求頭的Date值,并轉(zhuǎn)換成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);
// 返回指定的請(qǐng)求頭的值,沒(méi)有該頭,返回null,如果有多個(gè)相同名字的頭,則返回第一個(gè)該頭的值。name是大小寫無(wú)關(guān)。
public String getHeader(String name);
// 返回指定請(qǐng)求頭的所有值
public Enumeration<String> getHeaders(String name);
// 返回請(qǐng)求頭的所有名稱,有些Container會(huì)禁用該方法,此時(shí)返回null。
public Enumeration<String> getHeaderNames();
// 返回指定請(qǐng)求頭的int值。
public int getIntHeader(String name);
// 返回請(qǐng)求方法,如GET、POST、PUT等。該值在解析請(qǐng)求行結(jié)束后設(shè)置。
public String getMethod();
// 設(shè)置請(qǐng)求的額外path信息,該值在不同的地方會(huì)被設(shè)置為不同的值,如果請(qǐng)求的URI為: http://host:80/context/servlet/path/info如在HttpConnection中,其值為/context/servlet/path/info
// 在ContextHandler的doScope中,其值被設(shè)置為/servlet/path/info;在ServletHandler的doScope方法中,其值被設(shè)置為/path/info,并設(shè)置ServletPath為/servlet。
// 這里基于的假設(shè)為contextPath為/context,servletPath為/servlet,即在web.xml配置中Servlet相關(guān)的url-pattern為/servlet/*。
// 如果沒(méi)有/path/info后綴,則PathInfo為null,如果Servlet的path-pattern設(shè)置為/*,則ServletPath為"",對(duì)url-pattern為*.do類似的配置,pathInfo永遠(yuǎn)為null,servletPath則為/path/info.do的值;有些時(shí)候ServletPath的值也可能是Servlet Name的值。
public String getPathInfo();
public String getServletPath();
// 返回pathInfo對(duì)應(yīng)的在ServletContext下的真是Server路徑,如果pathInfo為null,則返回null。
public String getPathTranslated();
// 返回當(dāng)前請(qǐng)求所屬的ContextPath。它在ContextHandler的doScope方法中或Dispatcher的forward方法中設(shè)置。
public String getContextPath();
// 返回當(dāng)前請(qǐng)求的query字符串,如果設(shè)置了queryEncoding,使用該編碼方式編碼。
public String getQueryString();
// 返回請(qǐng)求的登陸用戶,如果用戶沒(méi)有認(rèn)證,則返回null。該信息從設(shè)置的Authentication實(shí)例中獲取,如果其實(shí)例為Authentication.Defered,則需要先驗(yàn)證,然后返回獲取UserIdentity,并從中獲取Principal,而從Principal中可以獲取用戶名。
public String getRemoteUser();
// 驗(yàn)證當(dāng)前請(qǐng)求的User是否在傳入的Role中,即使用設(shè)置的Authentication實(shí)例驗(yàn)證,當(dāng)前登陸的User所在的Roles中是否存在傳入的RoleName。其中UserIdentity.Scope實(shí)例用于查找RoleName對(duì)應(yīng)在Container使用的RoleName,如果沒(méi)有這個(gè)映射則使用傳入的RoleName本身,這個(gè)scope在ServletHandler的doScope方法中設(shè)置。這個(gè)映射在web.xml中的security-rol-ref(role-name->role-link)中設(shè)置。
public boolean isUserInRole(String role);
// 返回當(dāng)前登陸User的Principal實(shí)例,從設(shè)置的Authentication實(shí)例中獲取,或者為null。
public java.security.Principal getUserPrincipal();
// 返回客戶端指定的Session ID,它可以和Server的當(dāng)前Session ID不同。客戶端可以使用兩種方式設(shè)置該值:Cookie和URL。默認(rèn)先從Cookie中找,找不到再?gòu)腢RL中找。
// 對(duì)Cookie方式,在web.xml的cookie-config/name中配置Session的Cookie Name(默認(rèn)值為JSSESSIONID),找到該Cookie Name對(duì)應(yīng)的Cookie值作為Requested Session ID
// 對(duì)URL方式,在;<sessionIdPathParamName>=....[;#?/]之間的值作為Requested Session ID的值。其中sessionIdPathParamName可以通過(guò)web.xml的context-param,使用org.eclipse.jetty.servlet.SessionIdPathParameterName屬性值配置,默認(rèn)為jsessionid。
public String getRequestedSessionId();
public boolean isRequestedSessionIdFromCookie();
public boolean isRequestedSessionIdFromURL();
// 檢查當(dāng)前Requested Session Id是否valid,在Jetty中valid是指RequstedSessionId存在,并且和當(dāng)前請(qǐng)求的Server Session的ClusterId相同,即他們的SessionId相同。
public boolean isRequestedSessionIdValid();
// 返回/contextPath/servletPath/pathInfo的值。對(duì)forward后請(qǐng)求,該值為forward后的URI。
public String getRequestURI();
// 返回getSchema()://getServerName():getPort()/getRequestURI(),對(duì)forward后請(qǐng)求,該值為forward后的URL。
public StringBuffer getRequestURL();
// 返回和當(dāng)前請(qǐng)求相關(guān)聯(lián)的HttpSession,如果沒(méi)有關(guān)聯(lián)的HttpSession,且create為true,則創(chuàng)建新的HttpSession實(shí)例。沒(méi)有參數(shù)即create為true。
public HttpSession getSession(boolean create);
public HttpSession getSession();
//使用Container定義的認(rèn)證機(jī)制驗(yàn)證請(qǐng)求用戶的合法性。在Jetty實(shí)現(xiàn)中,只是對(duì)Authentication.Deferred的Authentication類型進(jìn)行驗(yàn)證,否則返回401 Unauthorized錯(cuò)誤相應(yīng),并返回false。
public boolean authenticate(HttpServletResponse response) throws IOException,ServletException;
// 提供用戶名和密碼并交由Container對(duì)其進(jìn)行認(rèn)證。在Jetty實(shí)現(xiàn)中,只對(duì)Authentication.Deferred類型提供用戶名和密碼認(rèn)證,否則拋出ServletException。
public void login(String username, String password) throws ServletException;
// 注銷當(dāng)前用戶,并清理_authentication字段。
public void logout() throws ServletException;
// 對(duì)multipart/form-data請(qǐng)求類型,表示請(qǐng)求內(nèi)容由多個(gè)部分組成,此時(shí)使用RFC1867來(lái)解析該內(nèi)容到多個(gè)Part中。在Servlet中一個(gè)Part有自己的請(qǐng)求頭和請(qǐng)求消息提,包含ContentType、Name、Headers、Size(已寫入的大小)、InputStream等信息,它還提供了一個(gè)方法將請(qǐng)求內(nèi)容寫入指定的文件中,以及刪除該內(nèi)部文件。并提供MultipartConfigElement類來(lái)做相關(guān)的配置,如寫文件時(shí)的位置Location;最大可上傳的文件大小MaxFileSize;最大可包含的所有Part的請(qǐng)求大小MaxRequestSize;如果寫入的數(shù)據(jù)超過(guò)配置的大小,則開(kāi)始將數(shù)據(jù)寫入文件中MaxFileSizeThreshold。該配置信息可以使用org.eclipse.multipartConfig屬性設(shè)置,而Location信息可以使用javax.servlet.context.tempdir屬性在ServletContext中設(shè)置,或這使用java.io.tmpdir中指定的值。在Jetty中使用MultiPartInputStream來(lái)表達(dá)并解析請(qǐng)求內(nèi)容到多個(gè)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;
}
除了以上實(shí)現(xiàn),Request還包含了一些額外的字段,如_timestamp在請(qǐng)求開(kāi)始時(shí)設(shè)置,即解析請(qǐng)求頭完成時(shí);_dispatchTime在RequestLogHandler的handle方法,當(dāng)Request是非Initial的狀態(tài)下設(shè)置,即當(dāng)前Request已經(jīng)為Dispatched的狀態(tài);_handled表示該請(qǐng)求是否已經(jīng)處理完成;// Servlet的四種驗(yàn)證方式(他們?cè)趙eb.xml文件中的long-config/auth-method中定義):
// BASIC使用Authentication頭傳輸認(rèn)證信息,該信息以Base64的編碼方式編碼,當(dāng)需要用戶驗(yàn)證時(shí)會(huì)彈出登陸窗口。
// FORM使用表單的方式認(rèn)證,用戶名和密碼在j_username和j_password字段中,登陸URL為/j_security_check,第一次使用表單方式明文傳輸,之后將認(rèn)證信息存放在Session中,在Session實(shí)效之前可以不用在手動(dòng)登陸。
// DIGEST也時(shí)使用Authentication頭傳輸認(rèn)證信息,但是它使用加密算法將密碼加密。
// CLIENT_CERT則使用客戶端證書的方式傳輸認(rèn)證信息。
// 更詳細(xì)的內(nèi)容參考: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";
// 返回當(dāng)前請(qǐng)求的認(rèn)證方法,可以是BASIC、FORM、CLIENT_CERT、DIGEST。如果沒(méi)有定義對(duì)應(yīng)Servlet的認(rèn)證配置,則返回null。在Jetty實(shí)現(xiàn)中,在ServletHandler根據(jù)配置信息以及認(rèn)證的結(jié)果設(shè)置請(qǐng)求的Authentication實(shí)例,如果Authentication實(shí)例是Authentication.Deffered實(shí)例,則先驗(yàn)證,并設(shè)置驗(yàn)證后的Authentication實(shí)例,如果Authentication實(shí)例是Authentication.User實(shí)例,則設(shè)置返回其AuthMethod屬性,否則返回null。
public String getAuthType();
// 返回當(dāng)前請(qǐng)求包含的Cookie數(shù)組,從請(qǐng)求的Cookie頭中獲取,如果沒(méi)有Cookie信息,則返回null。在Jetty實(shí)現(xiàn)中,使用CookieCutter類解析Cookie頭。
// Cookie值的格式為:<name>=value; [$path=..; $domain=...; $port=...; $version=..]; <name>=<value>.....
// 在Servlet中,Cookie類包含comment(描述信息)、domain(cookie只是對(duì)指定的domain域可見(jiàn),默認(rèn)情況下cookie只會(huì)傳輸給設(shè)置這個(gè)cookie的server)、maxAge(cookie的最長(zhǎng)可存活秒數(shù),負(fù)數(shù)表示永遠(yuǎn)不會(huì)失效,0表示cookie會(huì)被刪除)、path(指定那個(gè)path下的請(qǐng)求這個(gè)cookie才會(huì)被發(fā)送,包括它的子目錄)、secure(這個(gè)cookie是否只在https請(qǐng)求中發(fā)送)、version(0表示netscape最初定義的cookie標(biāo)準(zhǔn),1表示兼容RFC 2109,一般都是0以保持最大兼容性)、isHttpOnly(HttpOnly標(biāo)識(shí)的cookie一般不應(yīng)該暴露給客戶端的腳本,以部分解決跨站點(diǎn)腳本攻擊問(wèn)題)字段。對(duì)瀏覽器,一般他們應(yīng)該能為每個(gè)Web Server存儲(chǔ)至少20個(gè)cookie,總共至少能處理300個(gè)cookie,以及至少4k大小。
public Cookie[] getCookies();
// 解析指定請(qǐng)求頭的Date值,并轉(zhuǎn)換成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);
// 返回指定的請(qǐng)求頭的值,沒(méi)有該頭,返回null,如果有多個(gè)相同名字的頭,則返回第一個(gè)該頭的值。name是大小寫無(wú)關(guān)。
public String getHeader(String name);
// 返回指定請(qǐng)求頭的所有值
public Enumeration<String> getHeaders(String name);
// 返回請(qǐng)求頭的所有名稱,有些Container會(huì)禁用該方法,此時(shí)返回null。
public Enumeration<String> getHeaderNames();
// 返回指定請(qǐng)求頭的int值。
public int getIntHeader(String name);
// 返回請(qǐng)求方法,如GET、POST、PUT等。該值在解析請(qǐng)求行結(jié)束后設(shè)置。
public String getMethod();
// 設(shè)置請(qǐng)求的額外path信息,該值在不同的地方會(huì)被設(shè)置為不同的值,如果請(qǐng)求的URI為: http://host:80/context/servlet/path/info如在HttpConnection中,其值為/context/servlet/path/info
// 在ContextHandler的doScope中,其值被設(shè)置為/servlet/path/info;在ServletHandler的doScope方法中,其值被設(shè)置為/path/info,并設(shè)置ServletPath為/servlet。
// 這里基于的假設(shè)為contextPath為/context,servletPath為/servlet,即在web.xml配置中Servlet相關(guān)的url-pattern為/servlet/*。
// 如果沒(méi)有/path/info后綴,則PathInfo為null,如果Servlet的path-pattern設(shè)置為/*,則ServletPath為"",對(duì)url-pattern為*.do類似的配置,pathInfo永遠(yuǎn)為null,servletPath則為/path/info.do的值;有些時(shí)候ServletPath的值也可能是Servlet Name的值。
public String getPathInfo();
public String getServletPath();
// 返回pathInfo對(duì)應(yīng)的在ServletContext下的真是Server路徑,如果pathInfo為null,則返回null。
public String getPathTranslated();
// 返回當(dāng)前請(qǐng)求所屬的ContextPath。它在ContextHandler的doScope方法中或Dispatcher的forward方法中設(shè)置。
public String getContextPath();
// 返回當(dāng)前請(qǐng)求的query字符串,如果設(shè)置了queryEncoding,使用該編碼方式編碼。
public String getQueryString();
// 返回請(qǐng)求的登陸用戶,如果用戶沒(méi)有認(rèn)證,則返回null。該信息從設(shè)置的Authentication實(shí)例中獲取,如果其實(shí)例為Authentication.Defered,則需要先驗(yàn)證,然后返回獲取UserIdentity,并從中獲取Principal,而從Principal中可以獲取用戶名。
public String getRemoteUser();
// 驗(yàn)證當(dāng)前請(qǐng)求的User是否在傳入的Role中,即使用設(shè)置的Authentication實(shí)例驗(yàn)證,當(dāng)前登陸的User所在的Roles中是否存在傳入的RoleName。其中UserIdentity.Scope實(shí)例用于查找RoleName對(duì)應(yīng)在Container使用的RoleName,如果沒(méi)有這個(gè)映射則使用傳入的RoleName本身,這個(gè)scope在ServletHandler的doScope方法中設(shè)置。這個(gè)映射在web.xml中的security-rol-ref(role-name->role-link)中設(shè)置。
public boolean isUserInRole(String role);
// 返回當(dāng)前登陸User的Principal實(shí)例,從設(shè)置的Authentication實(shí)例中獲取,或者為null。
public java.security.Principal getUserPrincipal();
// 返回客戶端指定的Session ID,它可以和Server的當(dāng)前Session ID不同。客戶端可以使用兩種方式設(shè)置該值:Cookie和URL。默認(rèn)先從Cookie中找,找不到再?gòu)腢RL中找。
// 對(duì)Cookie方式,在web.xml的cookie-config/name中配置Session的Cookie Name(默認(rèn)值為JSSESSIONID),找到該Cookie Name對(duì)應(yīng)的Cookie值作為Requested Session ID
// 對(duì)URL方式,在;<sessionIdPathParamName>=....[;#?/]之間的值作為Requested Session ID的值。其中sessionIdPathParamName可以通過(guò)web.xml的context-param,使用org.eclipse.jetty.servlet.SessionIdPathParameterName屬性值配置,默認(rèn)為jsessionid。
public String getRequestedSessionId();
public boolean isRequestedSessionIdFromCookie();
public boolean isRequestedSessionIdFromURL();
// 檢查當(dāng)前Requested Session Id是否valid,在Jetty中valid是指RequstedSessionId存在,并且和當(dāng)前請(qǐng)求的Server Session的ClusterId相同,即他們的SessionId相同。
public boolean isRequestedSessionIdValid();
// 返回/contextPath/servletPath/pathInfo的值。對(duì)forward后請(qǐng)求,該值為forward后的URI。
public String getRequestURI();
// 返回getSchema()://getServerName():getPort()/getRequestURI(),對(duì)forward后請(qǐng)求,該值為forward后的URL。
public StringBuffer getRequestURL();
// 返回和當(dāng)前請(qǐng)求相關(guān)聯(lián)的HttpSession,如果沒(méi)有關(guān)聯(lián)的HttpSession,且create為true,則創(chuàng)建新的HttpSession實(shí)例。沒(méi)有參數(shù)即create為true。
public HttpSession getSession(boolean create);
public HttpSession getSession();
//使用Container定義的認(rèn)證機(jī)制驗(yàn)證請(qǐng)求用戶的合法性。在Jetty實(shí)現(xiàn)中,只是對(duì)Authentication.Deferred的Authentication類型進(jìn)行驗(yàn)證,否則返回401 Unauthorized錯(cuò)誤相應(yīng),并返回false。
public boolean authenticate(HttpServletResponse response) throws IOException,ServletException;
// 提供用戶名和密碼并交由Container對(duì)其進(jìn)行認(rèn)證。在Jetty實(shí)現(xiàn)中,只對(duì)Authentication.Deferred類型提供用戶名和密碼認(rèn)證,否則拋出ServletException。
public void login(String username, String password) throws ServletException;
// 注銷當(dāng)前用戶,并清理_authentication字段。
public void logout() throws ServletException;
// 對(duì)multipart/form-data請(qǐng)求類型,表示請(qǐng)求內(nèi)容由多個(gè)部分組成,此時(shí)使用RFC1867來(lái)解析該內(nèi)容到多個(gè)Part中。在Servlet中一個(gè)Part有自己的請(qǐng)求頭和請(qǐng)求消息提,包含ContentType、Name、Headers、Size(已寫入的大小)、InputStream等信息,它還提供了一個(gè)方法將請(qǐng)求內(nèi)容寫入指定的文件中,以及刪除該內(nèi)部文件。并提供MultipartConfigElement類來(lái)做相關(guān)的配置,如寫文件時(shí)的位置Location;最大可上傳的文件大小MaxFileSize;最大可包含的所有Part的請(qǐng)求大小MaxRequestSize;如果寫入的數(shù)據(jù)超過(guò)配置的大小,則開(kāi)始將數(shù)據(jù)寫入文件中MaxFileSizeThreshold。該配置信息可以使用org.eclipse.multipartConfig屬性設(shè)置,而Location信息可以使用javax.servlet.context.tempdir屬性在ServletContext中設(shè)置,或這使用java.io.tmpdir中指定的值。在Jetty中使用MultiPartInputStream來(lái)表達(dá)并解析請(qǐng)求內(nèi)容到多個(gè)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;
}
ServletResponse
ServletResponse是對(duì)Servlet響應(yīng)消息的封裝,其子接口HttpServletResponse則是對(duì)HTTP響應(yīng)消息的封裝,
ServletResponse接口定義如下:
public interface ServletResponse {
// 設(shè)置與返回消息體中的字符編碼方式。如果沒(méi)有設(shè)置,默認(rèn)使用ISO-8559-1。其中setContentType和setLocale會(huì)隱式的設(shè)置該值,但是顯示的設(shè)置會(huì)覆蓋之前隱式的設(shè)置,而該值的設(shè)置也會(huì)影響ContentType的值。該值的設(shè)置必須在調(diào)用getWriter()方法之前,否則不會(huì)起作用。在set方法中,如果傳入的charset為null,則清楚原來(lái)設(shè)置的值,并將原來(lái)的_mimeType值設(shè)置為Content-Type頭;否則,更新characterEncoding的值,并更新contentType的屬性以及Content-Type頭(更新contentType中"charset="之后部分的值,或使用"mimeType; charset=<characterType>")。
public void setCharacterEncoding(String charset);
public String getCharacterEncoding();
// 設(shè)置響應(yīng)消息的Content-Type頭以及contentType字段的值,它會(huì)同時(shí)影響mimeType字段以及characterEncoding字段。如果type為null,則清除contentType、mimeType的值,移除Content-Type響應(yīng)頭,并且如果locale也為null,同時(shí)清除characterType值;否則,設(shè)置mimeType為";"之前的值,而characterEncoding的值為"charset="之后的值,如果沒(méi)有"charset="之后的值,則是使用"charset=<characterType>"值拼接以設(shè)置Content-Type響應(yīng)頭。
public void setContentType(String type);
public String getContentType();
// 設(shè)置Locale的值,同時(shí)設(shè)置響應(yīng)的Content-Language頭。同時(shí)該set也會(huì)影響characterEncoding、mimeType和Content-Type響應(yīng)頭。其中characterEncoding從ContextHandler中的Locale到charset的映射中獲取,即web.xml中的locale-encoding-mapping-list中定義,而對(duì)Content-Type的值只影響"charset="之后的值。
public void setLocale(Locale loc);
public Locale getLocale();
// 設(shè)置contentLength字段以及Content-Length響應(yīng)消息頭。如果當(dāng)前寫入的數(shù)據(jù)已經(jīng)大于或等于設(shè)置的值,則該方法還會(huì)關(guān)閉當(dāng)前的ServletOutputStream或Writer。
public void setContentLength(int len);
// 返回向客戶端寫數(shù)據(jù)的ServletOutputStream或PrintWriter。它們只能使用其中一個(gè)方法。在Jetty中使用HttpOutput內(nèi)部封裝HttpGenerator實(shí)例用于向Socket中寫數(shù)據(jù)。
public ServletOutputStream getOutputStream() throws IOException;
public PrintWriter getWriter() throws IOException;
// 響應(yīng)消息處理相關(guān)的Buffer操作,在Jetty中即為HttpGenerator中響應(yīng)消息體的Buffer大小與刷新操作。設(shè)置BufferSize必須在寫響應(yīng)消息體之前。另外reset只是清除消息體的緩存,并不會(huì)清除響應(yīng)狀態(tài)碼、響應(yīng)頭等信息,如果該方法調(diào)用時(shí)消息已經(jīng)被commit,則拋出IllegalStateException。
public void setBufferSize(int size);
public int getBufferSize();
public void flushBuffer() throws IOException;
public void resetBuffer();
// 是否響應(yīng)消息已經(jīng)commit,即響應(yīng)消息狀態(tài)行和消息頭已經(jīng)發(fā)送給客戶端。
public boolean isCommitted();
// 清除所有的響應(yīng)消息狀態(tài),所有已經(jīng)設(shè)置的響應(yīng)頭(但是不包括Connection頭),如果響應(yīng)已經(jīng)commit,則拋出IllegalStateException。
public void reset();
}
HttpServletResponse接口定義如下,它添加了一些Cookie、Header相關(guān)的操作:// 設(shè)置與返回消息體中的字符編碼方式。如果沒(méi)有設(shè)置,默認(rèn)使用ISO-8559-1。其中setContentType和setLocale會(huì)隱式的設(shè)置該值,但是顯示的設(shè)置會(huì)覆蓋之前隱式的設(shè)置,而該值的設(shè)置也會(huì)影響ContentType的值。該值的設(shè)置必須在調(diào)用getWriter()方法之前,否則不會(huì)起作用。在set方法中,如果傳入的charset為null,則清楚原來(lái)設(shè)置的值,并將原來(lái)的_mimeType值設(shè)置為Content-Type頭;否則,更新characterEncoding的值,并更新contentType的屬性以及Content-Type頭(更新contentType中"charset="之后部分的值,或使用"mimeType; charset=<characterType>")。
public void setCharacterEncoding(String charset);
public String getCharacterEncoding();
// 設(shè)置響應(yīng)消息的Content-Type頭以及contentType字段的值,它會(huì)同時(shí)影響mimeType字段以及characterEncoding字段。如果type為null,則清除contentType、mimeType的值,移除Content-Type響應(yīng)頭,并且如果locale也為null,同時(shí)清除characterType值;否則,設(shè)置mimeType為";"之前的值,而characterEncoding的值為"charset="之后的值,如果沒(méi)有"charset="之后的值,則是使用"charset=<characterType>"值拼接以設(shè)置Content-Type響應(yīng)頭。
public void setContentType(String type);
public String getContentType();
// 設(shè)置Locale的值,同時(shí)設(shè)置響應(yīng)的Content-Language頭。同時(shí)該set也會(huì)影響characterEncoding、mimeType和Content-Type響應(yīng)頭。其中characterEncoding從ContextHandler中的Locale到charset的映射中獲取,即web.xml中的locale-encoding-mapping-list中定義,而對(duì)Content-Type的值只影響"charset="之后的值。
public void setLocale(Locale loc);
public Locale getLocale();
// 設(shè)置contentLength字段以及Content-Length響應(yīng)消息頭。如果當(dāng)前寫入的數(shù)據(jù)已經(jīng)大于或等于設(shè)置的值,則該方法還會(huì)關(guān)閉當(dāng)前的ServletOutputStream或Writer。
public void setContentLength(int len);
// 返回向客戶端寫數(shù)據(jù)的ServletOutputStream或PrintWriter。它們只能使用其中一個(gè)方法。在Jetty中使用HttpOutput內(nèi)部封裝HttpGenerator實(shí)例用于向Socket中寫數(shù)據(jù)。
public ServletOutputStream getOutputStream() throws IOException;
public PrintWriter getWriter() throws IOException;
// 響應(yīng)消息處理相關(guān)的Buffer操作,在Jetty中即為HttpGenerator中響應(yīng)消息體的Buffer大小與刷新操作。設(shè)置BufferSize必須在寫響應(yīng)消息體之前。另外reset只是清除消息體的緩存,并不會(huì)清除響應(yīng)狀態(tài)碼、響應(yīng)頭等信息,如果該方法調(diào)用時(shí)消息已經(jīng)被commit,則拋出IllegalStateException。
public void setBufferSize(int size);
public int getBufferSize();
public void flushBuffer() throws IOException;
public void resetBuffer();
// 是否響應(yīng)消息已經(jīng)commit,即響應(yīng)消息狀態(tài)行和消息頭已經(jīng)發(fā)送給客戶端。
public boolean isCommitted();
// 清除所有的響應(yīng)消息狀態(tài),所有已經(jīng)設(shè)置的響應(yīng)頭(但是不包括Connection頭),如果響應(yīng)已經(jīng)commit,則拋出IllegalStateException。
public void reset();
}
public interface HttpServletResponse extends ServletResponse {
// 向響應(yīng)消息中添加一個(gè)Cookie實(shí)例。即添加"Set-Cookie"頭。
public void addCookie(Cookie cookie);
// 消息頭操作:增刪改查。一個(gè)相同名字的頭可能會(huì)有多個(gè)條紀(jì)錄,因而可以對(duì)應(yīng)多個(gè)值。在Jetty實(shí)現(xiàn)中代理給HttpConnection中的responseFields字段。其中Date頭的格式為:EEE, dd MMM yyyy HH:mm:ss 'GMT',對(duì)于String為值的設(shè)置來(lái)說(shuō),處于INCLUDE Dispatch狀態(tài)下,只能設(shè)置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 String getHeader(String name);
public Collection<String> getHeaders(String name);
public Collection<String> getHeaderNames();
// 編碼傳入的url,決定并添加是否需要在URL中加入Session ID信息以作為Session追蹤。處于健壯性的考慮,所有Servlet產(chǎn)生的URL必須使用改方法編碼,不然對(duì)不支持Cookie的瀏覽器將會(huì)失去Session信息。在實(shí)現(xiàn)中,如果Request使用Cookie作為Session追蹤,則去除url中的sessionId信息;否則如果session存在并可用,則向url中添加session追蹤信息,在"#"或"?"之前。
public String encodeURL(String url);
// 編碼傳入的url,用于sendRedirect方法中。在Jetty中改方法直接調(diào)用encodeURL()方法。
public String encodeRedirectURL(String url);
// 向客戶端發(fā)送響應(yīng)狀態(tài)碼和原因(如果有的話)。服務(wù)器會(huì)保留已經(jīng)設(shè)置的cookie,但是會(huì)在必要情況下修改響應(yīng)頭。如果在web.xml中定義了響應(yīng)的狀態(tài)碼到error page的映射,則該響應(yīng)會(huì)被轉(zhuǎn)發(fā)到那個(gè)錯(cuò)誤頁(yè)面中。在Jetty實(shí)現(xiàn)中,它清除Buffer信息,characterEncoding值,Expires、Last-Modified、Cache-Control、Content-Type、Content-Length頭,設(shè)置響應(yīng)狀態(tài)和消息,對(duì)非204(No Content)、304(Not Modified)、206(Partial Content)、200(OK)的狀態(tài)碼(即允許有消息體),首先查找有沒(méi)有注冊(cè)的ErrorHandler ,如果有,向Request的屬性中設(shè)置javax.servlet.error.status_code, javax.servlet.error.message, javax.servlet.error.request_uri, javax.servlet.error.servlet_name為相應(yīng)的值(RequestDispatcher中定義的屬性key),并調(diào)用ErrorHandler的handle方法;否則設(shè)置Cache-Control頭為must-revalidate,no-cache,no-store,設(shè)置Content-Type頭為text/html;charset=ISO-8859-1,并返回一個(gè)簡(jiǎn)單的錯(cuò)誤頁(yè)面包含狀態(tài)碼和消息原因。最后調(diào)用HttpConnection的completeResponse()方法以完成響應(yīng)。
public void sendError(int sc, String msg) throws IOException;
public void sendError(int sc) throws IOException;
// 發(fā)送客戶端一個(gè)重定向的消息和URL,即設(shè)者響應(yīng)狀態(tài)碼為302 Moved Temporary,在Location中包含要重定向目的地的URL,此時(shí)客戶端會(huì)使用新的URL重新發(fā)送請(qǐng)求。如果location以"/"開(kāi)頭,表示它是絕對(duì)地址,否則為相應(yīng)請(qǐng)求的相對(duì)地址,即最終解析成Request.getRequestURI()/location,location可以包含query信息。最后調(diào)用HttpConnection的completeResponse()方法以完成當(dāng)前響應(yīng)。
public void sendRedirect(String location) throws IOException;
// 設(shè)置響應(yīng)狀態(tài)碼和狀態(tài)描述信息。
public void setStatus(int sc);
public void setStatus(int sc, String sm);
public int getStatus();
}
// 向響應(yīng)消息中添加一個(gè)Cookie實(shí)例。即添加"Set-Cookie"頭。
public void addCookie(Cookie cookie);
// 消息頭操作:增刪改查。一個(gè)相同名字的頭可能會(huì)有多個(gè)條紀(jì)錄,因而可以對(duì)應(yīng)多個(gè)值。在Jetty實(shí)現(xiàn)中代理給HttpConnection中的responseFields字段。其中Date頭的格式為:EEE, dd MMM yyyy HH:mm:ss 'GMT',對(duì)于String為值的設(shè)置來(lái)說(shuō),處于INCLUDE Dispatch狀態(tài)下,只能設(shè)置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 String getHeader(String name);
public Collection<String> getHeaders(String name);
public Collection<String> getHeaderNames();
// 編碼傳入的url,決定并添加是否需要在URL中加入Session ID信息以作為Session追蹤。處于健壯性的考慮,所有Servlet產(chǎn)生的URL必須使用改方法編碼,不然對(duì)不支持Cookie的瀏覽器將會(huì)失去Session信息。在實(shí)現(xiàn)中,如果Request使用Cookie作為Session追蹤,則去除url中的sessionId信息;否則如果session存在并可用,則向url中添加session追蹤信息,在"#"或"?"之前。
public String encodeURL(String url);
// 編碼傳入的url,用于sendRedirect方法中。在Jetty中改方法直接調(diào)用encodeURL()方法。
public String encodeRedirectURL(String url);
// 向客戶端發(fā)送響應(yīng)狀態(tài)碼和原因(如果有的話)。服務(wù)器會(huì)保留已經(jīng)設(shè)置的cookie,但是會(huì)在必要情況下修改響應(yīng)頭。如果在web.xml中定義了響應(yīng)的狀態(tài)碼到error page的映射,則該響應(yīng)會(huì)被轉(zhuǎn)發(fā)到那個(gè)錯(cuò)誤頁(yè)面中。在Jetty實(shí)現(xiàn)中,它清除Buffer信息,characterEncoding值,Expires、Last-Modified、Cache-Control、Content-Type、Content-Length頭,設(shè)置響應(yīng)狀態(tài)和消息,對(duì)非204(No Content)、304(Not Modified)、206(Partial Content)、200(OK)的狀態(tài)碼(即允許有消息體),首先查找有沒(méi)有注冊(cè)的ErrorHandler ,如果有,向Request的屬性中設(shè)置javax.servlet.error.status_code, javax.servlet.error.message, javax.servlet.error.request_uri, javax.servlet.error.servlet_name為相應(yīng)的值(RequestDispatcher中定義的屬性key),并調(diào)用ErrorHandler的handle方法;否則設(shè)置Cache-Control頭為must-revalidate,no-cache,no-store,設(shè)置Content-Type頭為text/html;charset=ISO-8859-1,并返回一個(gè)簡(jiǎn)單的錯(cuò)誤頁(yè)面包含狀態(tài)碼和消息原因。最后調(diào)用HttpConnection的completeResponse()方法以完成響應(yīng)。
public void sendError(int sc, String msg) throws IOException;
public void sendError(int sc) throws IOException;
// 發(fā)送客戶端一個(gè)重定向的消息和URL,即設(shè)者響應(yīng)狀態(tài)碼為302 Moved Temporary,在Location中包含要重定向目的地的URL,此時(shí)客戶端會(huì)使用新的URL重新發(fā)送請(qǐng)求。如果location以"/"開(kāi)頭,表示它是絕對(duì)地址,否則為相應(yīng)請(qǐng)求的相對(duì)地址,即最終解析成Request.getRequestURI()/location,location可以包含query信息。最后調(diào)用HttpConnection的completeResponse()方法以完成當(dāng)前響應(yīng)。
public void sendRedirect(String location) throws IOException;
// 設(shè)置響應(yīng)狀態(tài)碼和狀態(tài)描述信息。
public void setStatus(int sc);
public void setStatus(int sc, String sm);
public int getStatus();
}