概述
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();
}