Servlet容器原型(二)——一个简单的连接器

在(一)的基础上增加了,一个连接器,负责解析HTTP请求头,使servlet实例能够获得到请求头,cookie和请求参数/值等信息。

下面是这个应用程序的类图:

151929_X4to_265349.png

Bootstrap类很简单,代码如下:

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

就是负责启动连接器来监听客户端的连接,与(一)相比,这个启动的任务与监听连接的任务分割了开来。

而HttpConnector就是负责监听和处理这个连接的,在连接到来取得代表客户端的Socket之后,将其交给HttpProcessor。

从UML类图中可以看出,HttpProcessor要与ServletProcessor和StaticResourceProcessor组合在一起,那么究竟用那一个处理器去处理呢,就得先处理客户端的Request了,同样,这两个对象在到达程序员自己定义的Servlet之前已经实例化好,而在(二)里面就是HttpRequest和HttpRespose;那么在这之前先得做的工作就是parseRequest()和parseHeaders()了,它们都是HttpProcessor的私有函数。

下面来了解如何解析HTTP请求,主要分层五个部分:

  1. 读取套接字输入流;

  2. 解析请求行;

  3. 解析请求头

  4. 解析Cookie

  5. 获取参数

分别解释下上面的步骤:

  1. 获取套接字就是为了后面的使用而准备的,这里用org.apache.catalina.connector.http.SocketInputStream来包装以下原始的InputStream,用SocketInputStream的好处就是方便使用它的readRequestLine()方法和readHeader()方法。

  2. 解析请求行就是在parseRequest(SocketInputStream sis, OutputStream out)方法里面进行的,它解析出了HTTP请求的方法,URI,协议版本,还有查询字段(如果有的话),并将相应的信息填充到HttpRequest对象当中。这个原始的URI可能是相对路径,类似 /hostname/someResource,也可能是绝对路径,类似http://www.hostname.com/somResource,当然啦,做一些判断就OK,最后,规范化以下请求的URI

  3. 请求头信息有HttpHeader类表示,它在创建了之后传给SocketInputStream的readHeader()方法,若是有头信息可以读取,就将其填充。在parseHeaders()方法中,会有一个while循环不断读取HttpReader信息,如果读取到了,就调用request.addHeader(name, value)进行设置,如果没有了那么HttpHeader实例的属性字段nameEnd和nameValue都是0,由于这个InputStream是不支持读写位置的随意移动的,因此读取的方向会一直往下。

  4. Cookie是作为请求头的一部份发送的,字段类似 cookie:username=biu;password=qwert; 解析Tomcat提供的org.apache.catalina.util.RequestUtil类的parseCookieHeader()方法完成,这个方法放回一个Cookie[],每一个元素对于cookie中分号的一个字段。具体就是在上面的那个while循环中加一个if条件判断语句来处理

  5. 解析字段其实不是在这一步就完成的。而是在用户自定义的Servlet中调用getParameter(), getParameterMap(), getParameterNames(), getParameterValues()之后才进行的。这样有一个好处就是,减少解析参数带来的延迟,提高响应速度。参数值需要解析一次,而且也只会解析一次,同时不允许程序员去修改。比如说ParameterMap类,它是一个HashMap,利用一个boolean属性lock来限制修改操作。简单的示例如下:

public final class ParameterMap extends HashMap{
    
    private boolean locked = false;
    
    /* 一系列构造函数 */
    public ParameterMap(){
        super();
    }
    
    /* 比如说下面这个方法 */
    public Object put(Object key, Object value){
        if(locked)
            throw new IllegalStateException(sm.getString("parameterMap.locked"));
        return super.put(key, value);
    }
    
}

那么是如何控制只解析一次的呢?原因在parseParameters()方法中也有一个boolean字段parsed,当解析完成,参数会存储到对象变量parameters中,

if(parsed) return;

然后这个方法内部会创建一个名为result的ParameterMap变量,将其指向遍历parameters,若paramters==null,则会创建一个新的对象;接着就打开那个ParameterMap的锁了,setLocked(false);然后检查编码,就可以进行解析工作了,别忘了最后还有setLocked(true);


一个简单的Servlet容器就是初步这样~





转载于:https://my.oschina.net/zjh92119/blog/203672

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值