《How Tomcat Works》学习(四)——连接器(二)——解析http头部参数

前言

上文已经对路径及请求参数进行了解析,本文会对头部的参数,如cookie、content-length、content-type进行解析

 

程序

HttpRequest类增加以下代码:

public class HttpRequest implements HttpServletRequest {

	//......

	private int contentLength;
	private String contentType;
	protected ArrayList cookies =  new ArrayList();

	//......

	public void parseHeader() throws ServletException {
		int i = 0;
		byte[] buffer = new byte[2048];
		try {
			while (true) {
				input.read(buffer, i, 1);
				if (buffer[i] == '\r') {
					i++;
					input.read(buffer, i, 1);
					if (buffer[i] == '\n') {
						i++;
						input.read(buffer, i, 1);
						if (buffer[i] == '\r') {
							i++;
							input.read(buffer, i, 1);
							if (buffer[i] == '\n') {
								break;
							}
						}
					}
				}
				i++;
			}

			String header = new String(buffer);
			while (true) {
				int index = header.indexOf("\r\n");
				if (index > 0) {
					String line = header.substring(0, index);
					header = header.substring(index + 2);
					int index1 = line.indexOf(":");
					if (index1 > 0) {
						String key = line.substring(0, index1);
						String value = line.substring(index1 + 1).trim();
						if ("cookie".equals(key.toLowerCase())) {
							Cookie cookieArray[] = RequestUtil.parseCookieHeader(value);
							synchronized (cookies) {
								for (Cookie cookie : cookieArray) {
									cookies.add(cookie);
								}
							}
						} else if ("content-length".equals(key.toLowerCase())) {
							int n = -1;
							try {
								n = Integer.parseInt(value);
							} catch (Exception e) {
								throw new ServletException("httpProcessor.parseHeaders.contentLength");
							}
							this.setContentLength(n);
						} else if("content-type".equals(key.toLowerCase())) {
							this.setContentType(value);
						}
					}
				} else {
					break;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
			i = -1;
		}
	}

	public void setContentLength(int length) {
		this.contentLength = length;
	}
	
	@Override
	public int getContentLength() {
		return contentLength;
	}
	
	public void setContentType(String contentType) {
		this.contentType = contentType;
	}
	
	@Override
	public String getContentType() {
		return contentType;
	}

	@Override
	public Cookie[] getCookies() {
		synchronized (cookies) {
			if (cookies.size() < 1)
				return (null);
			Cookie results[] = new Cookie[cookies.size()];
			return ((Cookie[]) cookies.toArray(results));
		}
	}

	//......

}

第5-7行为本次要解析的http头部参数。11-33行代码写得比较憋足,主要是表示当遇到两个"\r\n"后,http头部内容结束。37-40行按行截取内容。41-44行根据“:”冒号分割key和value值。45-51行当key值为“cookie”时,通过RequestUtil.parseCookieHeader函数解析cookie并添加到HttpRequest的cookies数组中。在RequestUtil我们添加如下代码:

public class RequestUtil {
	//......

	public static Cookie[] parseCookieHeader(String header) {

		if ((header == null) || (header.length() < 1))
			return (new Cookie[0]);

		ArrayList cookies = new ArrayList();
		while (header.length() > 0) {
			int semicolon = header.indexOf(';');
			if (semicolon < 0)
				semicolon = header.length();
			if (semicolon == 0)
				break;
			String token = header.substring(0, semicolon);
			if (semicolon < header.length())
				header = header.substring(semicolon + 1);
			else
				header = "";
			try {
				int equals = token.indexOf('=');
				if (equals > 0) {
					String name = token.substring(0, equals).trim();
					String value = token.substring(equals + 1).trim();
					cookies.add(new Cookie(name, value));
				}
			} catch (Throwable e) {
				;
			}
		}

		return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
	}
}

多条cookie根据“;”分号进行分割,单条cookie根据“=”等号进行name和value的分割。

最后我们通过之前的ModernServlet.java获取cookies看看:

public class ModernServlet extends HttpServlet {

	public void init(ServletConfig config) {
		System.out.println("ModernServlet -- init");
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.print("HTTP/1.1 200 OK\r\n\r\n");

		out.println("");
		out.println("");
		out.println("Modern Servlet");
		out.println("");
		out.println("");

		out.println("

Method" + request.getMethod()); out.println("

Query String" + request.getQueryString()); out.println("

Request URI" + request.getRequestURI()); out.println("

Request Protocol" + request.getProtocol()); out.println("

Cookies

"); Cookie[] cookies = request.getCookies(); for(Cookie cookie : cookies) { out.println("
" + cookie.getName() + " : " + cookie.getValue()); } out.println(""); out.println(""); } }

之后启动程序,在chrome浏览器中按f12弹出开发者工具,然后在如下地方添加访问用cookie:

《How Tomcat Works》学习(四)——连接器(二)——解析http头部参数_第1张图片

之后访问对应servlet,http://localhost:8080/servlet/ModernServlet?username=jack&password=123456,得到

《How Tomcat Works》学习(四)——连接器(二)——解析http头部参数_第2张图片

成功获取cookies。content-length和content-type获取方式比较简单,就不列举了。

 

小结

通过两篇文章,简单描述了连接器解析http头部的过程。实际tomcat的处理更为复杂,代码封装也更好。希望通过本文抛砖引玉的方式,可以引导读者更深入学习tomcat的连接器源码。

你可能感兴趣的:(tomcat)