`
cwqcwq
  • 浏览: 75693 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

打造自己的web容器(4)

    博客分类:
  • j2ee
阅读更多
上一篇我们实现了一个极其简单的servlet容器,这一章,将在上一章的基础上进行扩充。我们以Tomcat的默认连接器为蓝本,逐步解剖连接器,虽然该连接器已不推荐使用,被性能更高的coyote所代替,但它仍是一个学习的好工具。

这一章,我们将完成如下任务:
1、解决上一篇的遗留问题
2、模拟Catalina的架构,重构我们的系统
3、实现HttpRequest的部分方法,包括:解析cookies,header,parameter等

这章的应用程序由三个模块组成:connector、startup和core,其中:
startup模块只有一个类,Bootstrap,用来启动应用的。
connector模块的类可以分为五组:
    * 连接器和它的支撑类(HttpConnector和HttpProcessor)。
    * 指代HTTP请求的类(HttpRequest)和它的辅助类。
    * 指代HTTP响应的类(HttpResponse)和它的辅助类。
    * Facade类(HttpRequestFacade和HttpResponseFacade)。
    * Constant类
core模块由两个类组成:ServletProcessor和StaticResourceProcessor。


启动类:Bootstrap

public final class Bootstrap {
	public static void main(String[] args) {
	        HttpConnector connector = new HttpConnector();
		connector.start();
	}
}



/**
 * 连接器
 *   1、等待HTTP请求
 * @author <a href="mailto:cwq2006@163.com">chen wen quan</a>
 *
 */
public class HttpConnector implements Runnable {

	// shutdown command
	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

	// the shutdown command received
	private boolean shutdown = false;

	private String scheme = "http";

	public String getScheme() {

		return scheme;
	}

	public void run() {

		ServerSocket serverSocket = null;
		int port = 8080;
		try {
			serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
		}
		catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
		// Loop waiting for a request
		while (!shutdown) {
			Socket socket = null;
			try {
				socket = serverSocket.accept();
			}
			catch (Exception e) {
				continue;
			}
			// Hand this socket off to an HttpProcessor
			HttpProcessor processor = new HttpProcessor();
			processor.process(socket);
		}
	}

	public void start() {
		Thread thread = new Thread(this);
		thread.start();
	}
}


/**
 * 连接器的支撑类,其主要作用为:
 *   1 接受前来的HTTP请求的套接字
 *   2 创建一个HttpRequest、HttpResponse对象
 *   3 解析HTTP请求的第一行和头部,并放到HttpRequest对象
 *   4 解析HttpRequest和HttpResponse对象到一个ServletProcessor或者 StaticResourceProcessor
 * @author <a href="mailto:cwq2006@163.com">chen wen quan</a>
 *
 */
public class HttpProcessor {

	private HttpRequest request = null;

	private HttpResponse response = null;

	private String requestContent = null;

	public void process(Socket socket) {

		InputStream input = null;
		OutputStream output = null;
		try {
			input = socket.getInputStream();
			output = socket.getOutputStream();
			// create HttpRequest object and parse
			request = new HttpRequest(input);
			// create HttpResponse object
			response = new HttpResponse(output);
			response.setRequest(request);
			response.setHeader("Server", "Pyrmont Servlet Container");
			parse(input);
			parseRequest(input, output);
			parseHeaders(input);
			//check if this is a request for a servlet or a static resource
			//a request for a servlet begins with "/servlet/"
			if (request.getRequestURI().startsWith("/servlet/")) {
				ServletProcessor processor = new ServletProcessor();
				processor.process(request, response);
			}
			else {
				StaticResourceProcessor processor = new StaticResourceProcessor();
				processor.process(request, response);
			}
			// Close the socket
			socket.close();
			// no shutdown for this application
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void parseHeaders(InputStream input) {

		// TODO Auto-generated method stub

	}

	public void parse(InputStream input) {

		// Read a set of characters from the socket
		StringBuffer request = new StringBuffer(2048);
		int i;
		byte[] buffer = new byte[2048];
		try {
			i = input.read(buffer);
		}
		catch (IOException e) {
			e.printStackTrace();
			i = -1;
		}
		for (int j = 0; j < i; j++) {
			request.append((char) buffer[j]);
		}
		System.out.println(request.toString());
		requestContent = request.toString();
	}

	private void parseRequest(InputStream input, OutputStream output) throws ServletException {

		String requestHeaderFirstLine = requestContent.substring(0, requestContent.indexOf("\n"));
		String headerInfo[] = requestHeaderFirstLine.split(" ");
		String method = headerInfo[0];
		String uri = headerInfo[1];
		String protocol = headerInfo[2];
		// Validate the incoming request line
		if (method.length() < 1) {
			throw new ServletException("Missing HTTP request method");
		}
		// Parse any query parameters out of the request URI
		int question = uri.indexOf("?");
		if (question >= 0) {
			request.setQueryString(uri.substring(question - 1));
			uri = uri.substring(0, question);
		}
		else {
			request.setQueryString(null);
		}
		// Checking for an absolute URI (with the HTTP protocol)
		if (!uri.startsWith("/")) {
			int pos = uri.indexOf("://");
			// Parsing out protocol and host name
			if (pos != -1) {
				pos = uri.indexOf('/', pos + 3);
				if (pos == -1) {
					uri = "";
				}
				else {
					uri = uri.substring(pos);
				}
			}
		}
		// Parse any requested session ID out of the request URI
		String match = ";jsessionid=";
		int semicolon = uri.indexOf(match);
		if (semicolon >= 0) {
			String rest = uri.substring(semicolon + match.length());
			int semicolon2 = rest.indexOf(';');
			if (semicolon2 >= 0) {
				request.setRequestedSessionId(rest.substring(0, semicolon2));
				rest = rest.substring(semicolon2);
			}
			else {
				request.setRequestedSessionId(rest);
				rest = "";
			}
			request.setRequestedSessionURL(true);
			uri = uri.substring(0, semicolon) + rest;
		}
		else {
			request.setRequestedSessionId(null);
			request.setRequestedSessionURL(false);
		}
		// Normalize URI (using String operations at the moment)
		String normalizedUri = normalize(uri);//检查URL是否合法
		// Set the corresponding request properties
		request.setMethod(method);
		request.setProtocol(protocol);
		if (normalizedUri != null) {
			request.setRequestURI(normalizedUri);
		}
		else {
			request.setRequestURI(uri);
		}
		if (normalizedUri == null) {
			throw new ServletException("Invalid URI: " + uri + "'");
		}

	}

	private String normalize(String uri) {

		if (uri == null)
			return null;

		// Create a place for the normalized path
		String normalized = uri;

		if (normalized.equals("/."))
			return "/";

		// Add a leading "/" if necessary
		if (!normalized.startsWith("/"))
			normalized = "/" + normalized;

		// Resolve occurrences of "//" in the normalized path
		while (true) {
			int index = normalized.indexOf("//");
			if (index < 0)
				break;
			normalized = normalized.substring(0, index) + normalized.substring(index + 1);
		}

		// Resolve occurrences of "/./" in the normalized path
		while (true) {
			int index = normalized.indexOf("/./");
			if (index < 0)
				break;
			normalized = normalized.substring(0, index) + normalized.substring(index + 2);
		}

		// Resolve occurrences of "/../" in the normalized path
		while (true) {
			int index = normalized.indexOf("/../");
			if (index < 0)
				break;
			if (index == 0)
				return (null); // Trying to go outside our context
			int index2 = normalized.lastIndexOf('/', index - 1);
			normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
		}

		// Return the normalized path that we have completed
		return (normalized);
	}
}


public class RequestUtil {

	/**
	 * 解析cookie
	 *   Cookie: userName=budi; password=pwd;
	 * @param header
	 * @return
	 */
	public static Cookie[] parseCookieHeader(String header) {

		if ((header == null) || (header.length() < 1))
			return (new Cookie[0]);
		List 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()]));
	}

	/**
	 * 解析请求参数
	 * @param map
	 * @param data 由 "userName=cwq&password=pwd".getBytes() 得
	 * @param encoding 字符编码
	 * @return
	 */
	public static void parseParameters(Map map, byte[] data, String encoding) throws UnsupportedEncodingException {

		if (data != null && data.length > 0) {
			int ix = 0;
			int ox = 0;
			String key = null;
			String value = null;
			while (ix < data.length) {
				byte c = data[ix++];
				switch ((char) c) {
					case '&':
						value = new String(data, 0, ox, encoding);
						if (key != null) {
							putMapEntry(map, key, value);
							key = null;
						}
						ox = 0;
						break;
					case '=':
						if (key == null) {
							key = new String(data, 0, ox, encoding);
							ox = 0;
						}
						else {
							data[ox++] = c;
						}
						break;
					case '+':
						data[ox++] = (byte) ' ';
						break;
					case '%':
						data[ox++] = (byte) ((convertHexDigit(data[ix++]) << 4) + convertHexDigit(data[ix++]));
						break;
					default:
						data[ox++] = c;
				}
			}
			//The last value does not end in '&'.  So save it now.
			if (key != null) {
				value = new String(data, 0, ox, encoding);
				putMapEntry(map, key, value);
			}
		}
	}

	private static void putMapEntry(Map map, String name, String value) {

		String[] newValues = null;
		String[] oldValues = (String[]) map.get(name);
		if (oldValues == null) {
			newValues = new String[1];
			newValues[0] = value;
		}
		else {
			newValues = new String[oldValues.length + 1];
			System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
			newValues[oldValues.length] = value;
		}
		map.put(name, newValues);
	}
	
    private static byte convertHexDigit( byte b ) {
        if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
        if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
        if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
        return 0;
    }
}
分享到:
评论

相关推荐

    QT-HTML.rar_QT_Qt webkit html_Qt5.4 Web容器_qt html

    5. **实例分析**:可能包含一些实际案例,如淘宝客户端如何利用QtWebKit实现特定的Web功能,展示如何在Qt应用中构建一个功能丰富的Web容器。 6. **最佳实践**:分享在开发过程中需要注意的问题,以及如何遵循最佳...

    改造Kuberntetes打造SAE容器云

    【标题】:“改造Kubernetes打造SAE容器云” 在现代云计算环境中,Kubernetes(K8s)已经成为容器编排的主流平台,它提供了一种高效、可扩展的方式来管理和部署容器化应用。SAE(Serverless Application Engine)是...

    java web workspace 02

    JSP页面会被Web容器转换为Servlet执行,生成动态内容。 3. **MVC框架(如Spring MVC)**:MVC(Model-View-Controller)是一种软件设计模式,广泛应用于Web开发中。Spring MVC是Spring框架的一部分,提供了一种组织...

    后端开发入门与实战教程:从0到1打造高效Web应用.zip

    这份"后端开发入门与实战教程:从0到1打造高效Web应用"的资源包,将引领初学者步入这一领域的殿堂,通过系统的学习和实践,使你能够构建出高效且稳定的Web应用。 首先,后端开发的基础知识主要包括编程语言、框架和...

    Web聊天室系统源码

    4. **MVC模式**:模型-视图-控制器架构模式在JavaWeb开发中广泛应用,它将业务逻辑、数据和用户界面分离,提高代码可读性和可维护性。聊天室系统中,模型负责处理数据,视图展示用户界面,控制器处理用户请求并协调...

    基于C++开发的WEB服务器,支持C++、Python、Java等多语言混合开发WEB应用.rar

    cppweb同时也是一个跨平台的微服务开发框架,通过两个核心组件webrouter与webserver提供微服务系统中的接口路由网关、服务注册中心、业务服务容器、定时任务调度中心以及接口...,我们的目标不是实现一个最好cgi容器...

    webcalendar.js控件及其使用说明

    4. **事件处理**:WebCalendar.js提供了丰富的事件处理机制,如点击日期、选择日期范围等,开发者可以绑定自定义函数来响应这些事件,增强日历的功能和交互性。 5. **多语言支持**:为了满足全球用户的需要,Web...

    Go-Cyclone是一个打造容器工作流的云原生持续集成持续发布平台

    7. **可视化界面**:提供友好的Web界面,方便用户查看构建状态、日志、历史记录等信息,同时也支持API接口,方便与其他工具集成。 8. **扩展性**:作为云原生平台,Go-Cyclone利用Kubernetes等容器编排工具,能够...

    小图片,用于web小图片,用于web

    在互联网世界中,小图片,特别是那些优化过的用于Web的小图片,扮演着至关重要的角色。它们不仅能够快速加载,提高用户体验,还能...在实际工作中,应根据具体需求和目标,灵活运用这些技巧,打造高效、美观的Web页面。

    《Java Web应用开发技术实用教程》-王红-电子教案

    《Java Web应用开发技术实用教程》是一本专为学习Java Web开发的初学者和进阶者编写的教材,由王红老师倾力打造。这本书涵盖了Java Web开发的基础知识到高级技术,旨在帮助读者掌握构建动态网页和Web应用程序的技能...

    深入体验Java Web开发内幕

    6. **Web容器与Tomcat**: 了解如何部署和管理Java Web应用在Web容器(如Apache Tomcat)中的运行,包括WAR文件的打包和发布。 7. **Web安全**: 书中可能会讨论关于身份验证、授权、防止SQL注入和XSS攻击等Web安全...

    阿里云 专有云企业版 V3.8.1 容器服务 产品简介 20200727

    该服务简化集群的搭建和扩容等运维工作,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳的Kubernetes容器化应用运行环境。 容器服务的优势包括: 1. 使用便捷:通过 Web 界面一键创建 Kubernetes 集群,...

    PHPWEB分享式智能网站管理系统 v1.2.0.rar

    我们将逐步为不同行业用户量身打造行业模板,让您以最快捷的方式,最低的成本创建功能强大的企业网站。PHPWEB是您高效便捷的网站管理工具。PHPWEB可免费用于非商业用途,无需购买授权,直接安装使用。商业用户可以...

    Tomcat+MySQL为自己的APP打造服务器(4)完结篇Demo

    "Tomcat+MySQL为自己的APP打造服务器(4)完结篇Demo"是一个完整的示例项目,展示了Android应用与自定义服务器端之间的交互流程。 首先,让我们了解Tomcat。Tomcat是一款开源的Java Servlet容器,它实现了Java EE中...

    Echarts + Web实现大屏展示效果(一)图片资源

    2. **准备DOM元素**:创建用于显示图表的div容器,并设置合适的宽高。 3. **初始化Echarts实例**:使用`echarts.init()`方法创建Echarts实例,关联到指定的DOM元素。 4. **配置图表**:定义图表的类型、数据、样式等...

    Web经典图表控件 Highcharts控件

    4. 初始化图表:在JavaScript中,通过`new Highcharts.Chart()`方法初始化图表,指定图表容器ID、配置对象和数据。 配置对象中可以包含以下关键参数: - `chart`: 图表的基本设置,如类型('line', 'column', 'pie...

    tomcat6.0 适用与java web 程序的开发服务器

    《深入理解Tomcat 6.0:打造高效Java Web服务器》 Tomcat 6.0是一款广泛应用的开源Java Web服务器,由Apache软件基金会维护,它实现了Java Servlet和JavaServer Pages(JSP)规范,为Java Web应用程序提供了一个轻...

    [HeyJava][天一时代][WEB开发]05_Tomcat搭建项目

    Tomcat是一个开源的Java Servlet容器,它能够解析并执行基于Java Servlet和JavaServer Pages (JSP) 的Web应用程序。此描述暗示了课程将涉及安装、配置Tomcat以及部署Web项目的基本步骤。 【标签】:“java web 开发...

    阿里云 专有云企业版 V3.12.0 容器服务Kubernetes版 产品简介 20200727

    容器服务简化集群的搭建和扩容等运维工作,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳的Kubernetes 容器化应用运行环境。 什么是容器服务 容器服务是高性能可伸缩的容器管理服务,支持企业级...

    Java_Web整合开发王者归来_10

    标签“java web 王者归来”暗示了该教程可能涵盖了一些高级主题,旨在将读者打造成Java Web开发的高手。 在Java Web开发中,整合开发通常涉及到多个技术和框架的协同工作,以构建高效、可维护的Web应用程序。以下是...

Global site tag (gtag.js) - Google Analytics