野生Web容器之TomJetty之掀起你的盖头来

上文我们对于实现这个野生Web容器做了一些知识铺垫和复习,息知了HTTP请求的头部的组成元素,本文我们主要讲解如何在代码中去解析它的各个部分,掀起盖头来,给哥笑一个^_^。在这之前,我们需要建立服务器,把框架搭建起来。

一、服务器建立

1.新建一个名为TomJetty的Java工程。

2.在工程src目录下新建一个tomjetty.config文件,用于提供服务器配置参数。

[java] view plaincopyprint?

  1. tomjetty.port=8080
  2. tomjetty.requestheader.class=cn.lynn.tomjetty.RequestHeaderParserImpl

3.编写一个工具类TomJettyUtil,用于程序读取配置参数值。

[java] view plaincopyprint?

  1. package cn.lynn.tomjetty;
  2. import java.io.IOException;
  3. import java.util.Properties;
  4. public class TomJettyUtil {
  5.     private static Properties props = new Properties();
  6.     static {
  7.         try {
  8.             props.load(TomJettyUtil.class.getClassLoader().getResourceAsStream(“tomjetty.config”));
  9.         } catch (IOException e) {
  10.             e.printStackTrace();
  11.             System.exit(0);
  12.         }
  13.     }
  14.     public static String getValue(String key) {
  15.         return props.getProperty(key);
  16.     }
  17. }

4.编写一个TomJetty类继承于Thread类,用于封装服务器对象

[java] view plaincopyprint?

  1. public class TomJetty extends Thread {
  2.     private static ServerSocket server;
  3.     private Socket socket;
  4.     public TomJetty(Socket socket) {
  5.         this.socket = socket;
  6.     }
  7.     …
  8. }

5.为TomJetty类提供openServer()和closeServer()方法,用户打开和关闭服务器。

[java] view plaincopyprint?

  1. public static void openServer() throws Exception {
  2.         server = new ServerSocket(Integer.parseInt(TomJettyUtil
  3.                 .getValue(“tomjetty.port”)));
  4.         while (true) {
  5.             new TomJetty(server.accept()).start();
  6.         }
  7.     }
  8. public static void closeServer() throws Exception {
  9.     if (server != null) {
  10.         if (!server.isClosed()) {
  11.             server.close();
  12.         }
  13.     }
  14. }

6.编写一个StartTomJetty类,用于开启服务器。

[java] view plaincopyprint?

  1. package cn.lynn.tomjetty;
  2. public class StartTomJetty {
  3.     public static void main(String[] args) {
  4.         try {
  5.             TomJetty.openServer();
  6.         } catch (Exception e) {
  7.             try {
  8.                 TomJetty.closeServer();
  9.             } catch (Exception e1) {
  10.                 e1.printStackTrace();
  11.             }
  12.         }
  13.     }
  14. }

这样以来,服务器就能运行了。当然,它什么也不会做。因为我们在run()方法中没有进行任何操作^_^。不要急,休息一下,后面就会讲到,另外代码部分也会随着章节的深入而重构。

二、HTTP请求头解析

1.编写一个RequestHeader类,用户封装请求头对象。

[java] view plaincopyprint?

  1. package cn.lynn.tomjetty;
  2. import java.util.HashMap;
  3. /**
  4.  * 封装请求头
  5.  * @author lynnli1229
  6.  */
  7. public class RequestHeader {
  8.     private String method;
  9.     private String url;
  10.     private String protocal;
  11.     private String accept;
  12.     private String accept_language;
  13.     private String user_agent;
  14.     private String accept_encoding;
  15.     private String ip;
  16.     private String port;
  17.     private String connection;
  18.     private String cookie;
  19.     // 存放请求头键值对
  20.     private HashMap<String, String> map;
  21.     // 存放请求头文本
  22.     private String txt;
  23.     // 省略getter()和setter()方法
  24.     @Override
  25.     public String toString() {
  26.         return “RequestHeader [” + “\n”
  27.             + method + ” ” + url + ” ” + protocal + “\n”
  28.             + “Accept: ” + accept + “\n”
  29.             + “Accept-Language: ” + accept_language + “\n”
  30.             + “User-Agent: ” + user_agent + “\n”
  31.             + “Accept-Encoding: ” + accept_encoding + “\n”
  32.             + “Host: ” + ip + “:” + port + “\n”
  33.             + “Connection: ” + connection + “\n”
  34.             + “Cookie: ” + cookie + “\n”
  35.             + “]”;
  36.     }
  37. }

2.设计一个IRequestHeaderParser接口,并声明parse()方法,用于解析请求头文本内容。

[java] view plaincopyprint?

  1. package cn.lynn.tomjetty;
  2. public interface IReqestHeaderParser {
  3.     public RequestHeader parse(String txt) throws Exception;
  4. }

3.提供IRequestHeaderParser接口的实现类RequestHeaderParserImpl,用于执行解析操作。注意一下,我们是以IE浏览器请求头为例,其他版本浏览器请求头的参数顺序会有变动,但整体差别不大。

[java] view plaincopyprint?

  1. package cn.lynn.tomjetty;
  2. import java.util.HashMap;
  3. public class RequestHeaderParserImpl implements IReqestHeaderParser {
  4.     /**
  5.      * 解析HTTP请求头
  6.      */
  7.     public RequestHeader parse(String txt) throws Exception {
  8.         RequestHeader header = new RequestHeader();
  9.         header.setTxt(txt);
  10.         // 截取请求头第一行
  11.         String firstLine = txt.substring(0, txt.indexOf(“\n”));
  12.         String method = firstLine.substring(0, firstLine.indexOf(” “));
  13.         String url = firstLine.substring(firstLine.indexOf(” “) + 1, firstLine.lastIndexOf(” “));
  14.         String protocal = firstLine.substring(firstLine.lastIndexOf(” “) + 1, firstLine.length());
  15.         header.setMethod(method);// 获取Accept参数值,存放到RequestHeader对象当中
  16.         header.setUrl(url);// 获取Accept参数值,存放到RequestHeader对象当中
  17.         header.setProtocal(protocal);// 获取Accept参数值,存放到RequestHeader对象当中
  18.         String[] lines = txt.split(“\n”);
  19.         HashMap<String, String> map = new HashMap<String, String>();
  20.         // 从请求头第二行开始分隔,因为第一行没有冒号
  21.         for (int i = 1; i < lines.length; i++) {
  22.             map.put(lines[i].split(“: “)[0], lines[i].split(“: “)[1]);
  23.         }
  24.         header.setMap(map);
  25.         header.setAccept(map.get(“Accept”)); // 获取Accept参数值,存放到RequestHeader对象当中
  26.         header.setAccept_language(map.get(“Accept-Language”)); // 获取Accept-Language参数值,存放到RequestHeader对象当中
  27.         header.setUser_agent(map.get(“User-Agent”)); // 获取User-Agent参数值,存放到RequestHeader对象当中
  28.         header.setAccept_encoding(map.get(“Accept-Encoding”)); // 获取Accept-Encoding参数值,存放到RequestHeader对象当中
  29.         header.setIp(map.get(“Host”).split(“:”)[0]); // 获取Host参数的IP参数值,存放到RequestHeader对象当中
  30.         header.setPort(map.get(“Host”).split(“:”)[1]); // 获取Host参数的Port参数值,存放到RequestHeader对象当中
  31.         header.setConnection(map.get(“Connection”)); // 获取Connection参数值,存放到RequestHeader对象当中
  32.         header.setCookie(map.get(“Cookie”)); // 获取Cookie参数值,存放到RequestHeader对象当中
  33.         return header;
  34.     }
  35. }

4.在TomJetty类的run()方法中加入如下代码,用于将解析请求头的结果打印到控制台上。

[java] view plaincopyprint?

  1. try {
  2.     InputStream in = socket.getInputStream(); // 获取客户端发送的字节流
  3.     OutputStream out = socket.getOutputStream(); // 获取服务端响应的字节流
  4.     byte[] b = new byte[1024 * 1024]; // 设置字节缓冲区
  5.     in.read(b); // 读取客户端字节流(字节流的请求头)
  6.     String txt = new String(b).trim(); // 将请求头封装成String,准备交给解析器解析
  7.     IReqestHeaderParser parser = (IReqestHeaderParser) Class.forName(TomJettyUtil.getValue(“tomjetty.requestheader.class”)).newInstance();
  8.     RequestHeader header = parser.parse(txt); // 终于可以解析了
  9.     System.out.println(header);
  10. } catch (Exception e) {
  11.     e.printStackTrace();
  12. }

三、运行效果展示

在IE浏览器中输入上述地址后,控制台打印如下:

[java] view plaincopyprint?

  1. RequestHeader [
  2. GET /hello.jsp HTTP/1.1
  3. Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/QVOD, application/QVOD, */*
  4. Accept-Language: zh-cn
  5. User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; KB974488)
  6. Accept-Encoding: gzip, deflate
  7. Host: 127.0.0.1:8080
  8. Connection: Keep-Alive
  9. Cookie: null
  10. ]

写到这里,我们终于完成了对HTTP请求头的解析操作,代码比预想的要多,其中字符串的拆分是关键,其他也没什么了。

标签