上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0

          概述

          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

          ServletRequest是對Servlet請求消息的封裝,其子接口HttpServletRequest則是對HTTP請求消息的封裝,在Servlet框架中默認實現了ServletRequestWrapper和HttpServletRequestWrapper以便利用戶對請求的Wrap。

          在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

          ServletResponse是對Servlet響應消息的封裝,其子接口HttpServletResponse則是對HTTP響應消息的封裝,

          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);
              public Locale getLocale();

              // 設置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 String getHeader(String name); 
              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();
          }
          posted on 2014-05-16 01:29 DLevin 閱讀(6032) 評論(2)  編輯  收藏 所屬分類: Jetty

          FeedBack:
          # re: 深入Jetty源碼之Servlet框架及實現(ServletRequest、ServletResponse)
          2014-05-17 15:07 | 問問
          拉了  回復  更多評論
            
          # re: 深入Jetty源碼之Servlet框架及實現(ServletRequest、ServletResponse)
          2014-05-17 15:08 | 問問
          發  回復  更多評論
            
          主站蜘蛛池模板: 灯塔市| 嘉荫县| 西城区| 弥渡县| 兴山县| 白城市| 梧州市| 浦北县| 拜泉县| 怀远县| 邹城市| 宁津县| 广宁县| 湖南省| 馆陶县| 北京市| 临高县| 巨野县| 清徐县| 龙井市| 靖边县| 蓝山县| 同江市| 达日县| 来凤县| 古交市| 平果县| 卓资县| 博白县| 凤山市| 封开县| 乡城县| 定州市| 磐安县| 南平市| 陆丰市| 湘潭县| 黄冈市| 祁东县| 汝城县| 莒南县|