Zhiqim Httpd即知启蒙WEB容器,是Zhiqim Framework面向WEB开发的多例服务,提供更简洁配置、积木式组件模块和天然的模型模板设计。

森中灵 最后提交于1月前 增加RedirectContext方便配置HTTP:80跳转到HTTPS:443
HttpHeaderAbs.java31KB
/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙,邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd;

import java.io.EOFException;
import java.io.IOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map.Entry;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLHandshakeException;

import org.zhiqim.httpd.constants.HttpStep;
import org.zhiqim.httpd.context.RedirectContext;
import org.zhiqim.httpd.model.HttpDomainPortPath;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.model.HttpLine;
import org.zhiqim.kernel.model.codes.URI;
import org.zhiqim.kernel.model.maps.HashMapSS;
import org.zhiqim.kernel.model.seqs.Sequence;
import org.zhiqim.kernel.util.Ints;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;

/**
 * HTTP头部抽象类
 *
 * @version v1.0.0 @author zouzhigang 2018-9-11 新建与整理
 */
public abstract class HttpHeaderAbs extends HttpHeaderInner implements HttpHeader
{
    private static final Log log = LogFactory.getLog("http.request");
    private static final Sequence sequence = new Sequence(6);
    
    //连接&监听&输入输出流&发送器
    private HttpConnection conn;
    private HttpListener listener;
    private HttpServer server;
    
    private HttpInputStream input;
    private HttpOutputStream output;
    private HttpSenderImpl sender;
    
    //编号&步骤&时间
    private String requestId;
    private int requestStep;
    private long requestTimeMillis;
    
    //状态
    private String statusLine;
    private String method;
    private URI uri;
    private String version;
    
    //属性
    private HashMapSS headerMap;
    private String x_Forwarded_For;
    private String mimeType;
    private String characterEncoding;
    private X509Certificate[] certs;
    
    //关联对象
    private HttpContext context;
    
    //请求&响应&处理器
    private HttpRequestAbs request;
    private HttpResponseImpl response;
    private HttpHandler handler;
    private HttpDomainPortPath domainPortPath;
    
    //临时路径值
    private transient String pathInContext;
    private transient String pathInRMI;
    private transient String pathOnResource;
    
    //解释头
    private HttpLine liner;

    /** 构造函数 */
    public HttpHeaderAbs (HttpConnection conn, HttpInputStream input, SSLEngine sslEngine)
    {
        this.conn = conn;
        this.listener = conn.getListener();
        this.server = listener.getServer();
        this.input = input;
        this.output = new HttpOutputStream(conn);
        this.sender = newSender();
        
        this.requestId = conn.getId() + sequence.nextString();
        this.requestStep = _01_CREATE_;
        this.requestTimeMillis = System.currentTimeMillis();
        
        this.liner = new HttpLine();
        this.headerMap = new HashMapSS();
    }
    
    /*****************************************************************************/
    //子类必须实现的抽象方法
    /*****************************************************************************/
    
    /** 创建发送器 */
    public abstract HttpSenderImpl newSender();
    
    /** 创建请求 */
    public abstract HttpRequestAbs newRequest();
    
    /** 是否BIO */
    public abstract boolean isBio();
    
    /*****************************************************************************/
    //子类必须实现的抽象方法
    /*****************************************************************************/
    
    public HttpServer getServer()
    {
        return server;
    }
    
    public HttpContext getContext()
    {
        return context;
    }
    
    public HttpInputStream getInputStream()
    {
        return input;
    }
    
    public HttpOutputStream getOutputStream()
    {
        return output;
    }
    
    public String getId()
    {
        return requestId;
    }
    
    public long getReceiveTimeMillis()
    {
        return requestTimeMillis;
    }
    
    public HttpListener getListener()
    {
        return listener;
    }
    
    public HttpResponse getResponse()
    {
        return response;
    }
    
    @Override /** 获取连接中的日志对象 */
    public Log getLog()
    {
        return log;
    }
    
    @Override /** 获取发送器 */
    public HttpSender getSender()
    {
        return sender;
    }
    
    /*****************************************************************************/
    //执行请求
    /*****************************************************************************/
    
    /** 
     * 执行缓冲请求
     * 
     * @exception HttpException     HTTP异常
     * @exception IOException       IO异常
     */
    public void execute() throws HttpException, IOException
    {
        try
        {
            //2.第二步,从流中解析消息头
            if (requestStep < _02_PARSE_HEADER_)
            {
                int code = liner.parse(input);
                if (code == _100_CONTINUE_)
                {//解析未完成情况,等待下次execute
                    return;
                }
                
                if (code != _0_SUCCESS_)
                {//解析出错
                    throw new HttpException(code);
                }
                
                setStep(_02_PARSE_HEADER_);
            }
            
            //3.第三步,检查消息头
            if (requestStep < _03_CHECK_HEADER_)
            {
                parseHeader();
                sender.setVersion(getVersion());
                if (!isBio())
                {//非BIO根据请求来判断
                    sender.setHeader(_CONNECTION_, getHeader(_CONNECTION_));
                }
                setStep(_03_CHECK_HEADER_);
            }
            
            //4.第五步,查询上下文环境
            if (requestStep < _04_QUERY_CONTEXT_)
            {
                String host = getHostOnly();
                if (Validates.isEmpty(host))
                    throw new HttpException(_412_PRECONDITION_FAILED_);
                
                String path = getPath();
              
                //4.1检查完全匹配,如http://www.zhiqim.com/example 对应到/example上下文环境
                context = server.getContext(host, path);
                if (context != null && !"/".equals(path))
                {//如果找到Context,则认为path是虚拟目录/example,重定向到Context的根http://example.zhiqim.com/
                    sender.sendRedirect(path+"/");
                    return;
                }
                
                if (context == null)
                {//4.2检查是否有虚拟目录上下文环境
                    String maybe = getVirtualDirectory();//虚拟目录
                    context = server.getContext(host, maybe);
                    if (context == null)
                    {//不是虚拟目录上下文环境
                        context = server.getContext(host, "/");
                    }
                }
                
                if (context == null)
                {//4.3未找到则表示未配置该上下文环境,返回未找到
                    sender.sendError(_404_NOT_FOUND_);
                    return;
                }
                
                if (context instanceof RedirectContext)
                {//4.4 重定向上下文环境,直接跳转
                    sender.sendRedirect(((RedirectContext)context).getResourcePath());
                    return;
                }
                
                domainPortPath = new HttpDomainPortPath().parse(this);
                setStep(_04_QUERY_CONTEXT_);
            }
            
            //5.第五步,在上下文环境中查找处理器
            if (requestStep < _05_QUERY_HANDLER_)
            {
                parsePathInContext(context);
                String pathInContext = getPathInContext();
                if ("/".equals(pathInContext))
                {//5.1路径为根路径,但没匹配到欢迎页,返回未找到
                    sender.sendError(_404_NOT_FOUND_);
                    return;
                }
                
                if (context.isFilterPath(pathInContext))
                {//5.2路径为过滤地址,返回禁止访问
                    sender.sendError(_403_FORBIDDEN_);
                    return;
                }
                
                //指定输出块大小
                output.setChunkSize(context.getChunkSize());
                
                handler = context.getMatchHandler(pathInContext);
                if (handler == null)
                {//5.3未找到匹配的处理器
                    context.handleResource(this, sender);
                    return;
                }
                
                if (isMethodHead())
                {//5.4针对HEAD的请求直接返回成功即可
                    sender.sendHeader(_200_OK_);
                    return;
                }
                
                if (isMethodOptions())
                {//5.5针对OPTIONS请求的处理
                    context.getOptionsHandler().handle(this, sender);
                    sender.commit();
                    return;
                }
                
                if (handler instanceof HttpEntity)
                {//5.6找到是实体处理器
                    ((HttpEntity)handler).handle(this, sender);
                    sender.commit();
                    return;
                }
                
                setStep(_05_QUERY_HANDLER_);
            }
        
            //6.第六步,解析GET,POST COOKIE,SESSION和内容等信息
            if (requestStep < _06_PARSE_CONTENT_)
            {
                if (request == null)
                    request = newRequest();
                
                if (response == null)
                    response = new HttpResponseImpl(this.request);
                
                request.parseHeaderByContextOK();
                request.parseGetPostCookieSession();
                if (!request.parseContent())
                    return;
                
                setStep(_06_PARSE_CONTENT_);
            }
            
            //7.第七步,转入处理器进行处理
            if (requestStep < _07_HANDLER_BEGIN_)
            {
                //BEGIN
                setStep(_07_HANDLER_BEGIN_);
                
                HttpExecutor executor = (HttpExecutor)handler;
                executor.handle(request, response);
                
                //END
                if (requestStep < _10_HANDLER_END_)
                    setStep(_10_HANDLER_END_);
            }
            
            //12.第十二步,提交
            if (!isCommitted())
            {
                if (response != null)
                    response.commit();
                else
                    sender.commit();
            }
            
            if (isWebSocket())
            {//13.最后判断是否是WebSocket,在提交后开启WebSocket回调
                conn.doWebSocketOpen();
            }
        }
        catch (SSLHandshakeException e) 
        {//SSL握手异常
            close();
        }
        catch(EOFException e)
        {//没读到消息
            try{sender.sendError(_400_BAD_REQUEST_);}catch(Exception e2){close();}
        }
        catch(SocketTimeoutException | SocketException e)
        {//SOCKET异常
            try{sender.sendError(_408_REQUEST_TIMEOUT_);}catch(Exception e2){close();}
        }
        catch(HttpException e)
        {//HTTP异常
            if (e.getCode() == _444_INTERRUPT_)
                close();//直接关闭
            else
                try{sender.sendError(e.getCode());}catch(Exception e2){close();}
        }
        catch(IOException e)
        {//IO异常
            close();
        }
        catch(Throwable e)
        {//未知服务端异常
            log.error(e);
            try{sender.sendError(_500_INTERNAL_SERVER_ERROR_);}catch(Exception e2){close();}
        }
        finally
        {
            if (isCommitted())
            {//已提交和关闭状态的清理及日志打印
                setStep(_12_FINISHED_);
                
                listener.finished(this);
                
                destroy();
                
                if (sender != null){
                    sender.destroy();
                }
                
                if (request != null){
                    request.destroy();
                }
                if (response != null){
                    response.destroy();
                }
            }
        }
    }
    
    /** 解析全路径path,生成上下文环境中绝对路径  */
    public void parsePathInContext(HttpContext context)
    {
        this.context = context;
        String contextPath = context.getContextPath();
        
        if ("/".equals(contextPath))
            pathInContext = uri.getPath();
        else
            pathInContext = uri.getPath().substring(contextPath.length());
        pathInContext = Strings.addStartsWith(pathInContext, "/");
        
        if ("/".equals(pathInContext))
        {//如果是根地址,则加上可能的welcomeUrl
            if (Validates.isNotEmptyBlank(context.getWelcomeUrl()))
                pathInContext = context.getWelcomeUrl();
        }
        
        //设置编码格式
        if (Validates.isEmptyBlank(characterEncoding))
            characterEncoding = context.getDefaultEncoding();
        
        if (Validates.isEmptyBlank(characterEncoding))
            characterEncoding = _UTF_8_;
    }
    
    /**
     * 分析请求行和请求头信息
     * 
     * @param headerList    消息头列表,不为空
     * @exception EOFException 流结束异常
     * @exception HttpException HTTP请求异常
     */
    public void parseHeader() throws EOFException, HttpException
    {
        List<String> headerList = liner.list();
        
        //第一步,读头部流,得到头部列表
        if (headerList.isEmpty())
            throw new EOFException();
        
        //第二步,检查首行格式,得到HTTP版本,方法和URL,如GET /index.htm HTTP/1.1
        String line = statusLine = headerList.get(0).trim();
        if (line.length() > _MAX_LINE_LEN_)
            throw new HttpException(_414_REQUEST_URL_TOO_LARGE_);

        int ind = line.indexOf(' ');
        if (ind == -1)
            throw new HttpException(_400_BAD_REQUEST_);
        
        //2.1,判断方法是不是支持的GET,POST和HEAD,PUT,DELETE,OPTIONS
        method = line.substring(0, ind);
        if (!(_GET_.equalsIgnoreCase(method) || _POST_.equalsIgnoreCase(method) || _HEAD_.equalsIgnoreCase(method) || _PUT_.equalsIgnoreCase(method) || _DELETE_.equalsIgnoreCase(method) || _OPTIONS_.equalsIgnoreCase(method)))
            throw new HttpException(_405_METHOD_NOT_ALLOWD_);
        method = method.toUpperCase();
        
        line = line.substring(ind+1).trim();
        ind = line.indexOf(' ');
        if (ind == -1)
            throw new HttpException(_400_BAD_REQUEST_);
        
        //2.2,读取URL
        String url = line.substring(0, ind);
        if (url == null || url.trim().length()<1)
            throw new HttpException(_400_BAD_REQUEST_);
        
        if (!url.startsWith("/") || url.indexOf("/..") != -1 || url.indexOf("../") != -1)//如果出现目录相关的,直接拒绝
            throw new HttpException(_400_BAD_REQUEST_);
        
        //2.3,解析URL成URI
        uri = new URI();
        if (!uri.parse(url.trim()).isParsed())
            throw new HttpException(_400_BAD_REQUEST_);
        
        //2.4,判断HTTP版本是否是支持的1.0/1.1
        version = line.substring(ind+1).trim();
        if (!_HTTP_1_1_.equals(version) && !_HTTP_1_0_.equals(version))
            throw new HttpException(_505_VERSION_NOT_SUPPORTED_);

        //第三步,读取其他头部信息
        for (int i=1;i<headerList.size();i++)
        {
            line = headerList.get(i);
            ind = line.indexOf(':');
            if (ind == -1)
                continue;
            
            String key = Strings.trim(line.substring(0, ind));
            String value = Strings.trim(line.substring(ind+1));
            setHeader(key, value);
        }
        
        //第四步,检查是否是代理模式
        
        //4.1,如果不是代理模式则返结束
        x_Forwarded_For = getHeader(_X_FORWARDED_FOR_);
        
        //4.2,把真实HOST放置到_HOST_
        String x_Forwarded_Host = getHeader(_X_FORWARDED_HOST_);
        if (Validates.isNotEmptyBlank(x_Forwarded_Host))
            setHeader(_HOST_, x_Forwarded_Host);
        
        //第五步,验证HOST是否是服务端支持的
        String hostPort = getHostPort();
        if (hostPort == null)
            throw new HttpException(_412_PRECONDITION_FAILED_);
        
        //第六步,获取内容格式和编码,如果不存在内容类型则默认为UTF-8
        String contentType= getHeader(_CONTENT_TYPE_);
        if (contentType != null)
        {
            //在contentType中查找encoding,格式为:text/html; charset=UTF-8; boundary=-------123456789
            int i0 = contentType.indexOf(';');
            if (i0 == -1)
                mimeType = contentType.toLowerCase();
            else
            {
                mimeType = contentType.substring(0, i0).trim().toLowerCase();
                int i1 = contentType.indexOf("charset=", i0);
                if (i1>=0)
                {
                    int i2 = contentType.indexOf(";", i1);
                    if (i2 == -1)
                        characterEncoding = contentType.substring(i1 + 8);
                    else
                        characterEncoding = contentType.substring(i1 + 8, i2).trim();
                }
            }
        }
    }
    
    /*******************************************************************************/
    //步骤
    /*******************************************************************************/
    
    public void setStep(int step)
    {
        if (this.requestStep >= step)
            return;
        
        this.requestStep = step;
    }
    
    public int getStep()
    {
        return requestStep;
    }
    
    public String getStepDesc()
    {
        return HttpStep.getStatusMsg(requestStep);
    }
    
    public boolean isRead()
    {//头部已读
        return requestStep > _03_CHECK_HEADER_;
    }
    
    public boolean isParsed()
    {//内部已读
        return requestStep > _06_PARSE_CONTENT_;
    }
    
    public boolean isHandled()
    {//处理完成
        return requestStep >= _10_HANDLER_END_;
    }
    
    public boolean isCommitted()
    {//提交完成
        return requestStep >= _11_COMMITTED_;
    }
    
    public boolean isEditable()
    {//正在提交即不可编辑
        return !isCommitted();
    }
    
    /** 关闭连接 */
    public void close()
    {//由sender回调
        if (!isCommitted())
            setStep(_11_COMMITTED_);
        
        conn.close();
    }
    
    /***********************************************************************/
    //获取和判断请求行信息,包括协议、方法、版本、URI等
    /***********************************************************************/
    
    @Override /** 获取客户端IP地址 */
    public String getRemoteAddr()
    {
        //如果是代理,取代理第一个IP
        if (x_Forwarded_For != null)
            return x_Forwarded_For.split(",")[0];
        else//否则取连接
            return conn.getRemoteAddr();
    }
    
    /** 获取客户端端口 */
    public int getRemotePort()
    {
        return conn.getRemotePort();
    }
    
    @Override 
    public int getListenerPort()
    {
        return conn.getListener().getPort();
    }
    
    @Override /** 获取请求行 */
    public String getHeaderLine()
    {
        return statusLine;
    }
    
    @Override /** 获取请求版本 */
    public String getVersion()
    {
        return version;
    }
    
    @Override /** 获取请求方法 */
    public String getMethod()
    {
        return method;
    }
    
    @Override /** 是否是HEAD方法 */
    public boolean isMethodHead()
    {
        return _HEAD_.equals(method);
    }
    
    @Override /** 是否OPTIONS方法 */
    public boolean isMethodOptions()
    {
        return _OPTIONS_.equals(method);
    }
    
    @Override /** 是否PUT方法 */
    public boolean isMethodPut()
    {
        return _PUT_.equals(method);
    }
    
    @Override /** 是否DELETE方法 */
    public boolean isMethodDelete()
    {
        return _DELETE_.equals(method);
    }
    
    @Override /** 是否是GET方法 */
    public boolean isMethodGet()
    {
        return _GET_.equals(method);
    }
    
    @Override /** 是否是POST方法 */
    public boolean isMethodPost()
    {
        return _POST_.equals(method);
    }
    
    @Override /** 判断是否需要响应内容 */
    public boolean isMethodResponseContent()
    {//GET,POST,其他的PUT,DELETE认为是增加和删除,只响应状态
        return _GET_.equals(method) || _POST_.equals(method);
    }
    
    /** 获取URI信息 */
    public URI getUri()
    {
        return uri;
    }
    
    @Override /** 获取URI路径,以/开头,如/test.html,如果没有文件后缀则为'/' */
    public String getPath()
    {
        return (uri == null)?"":uri.getPath();
    }
    
    @Override /** 获取查询串 */
    public String getQueryString()
    {
        return uri.getQuery();
    }
    
    @Override /** 获取URI虚拟目录信息 */
    public String getVirtualDirectory()
    {
        return uri.getVirtualDirectory();
    }
    
    @Override /** 获取头部信息 */
    public HashMapSS getHeaderMap()
    {
        return headerMap;
    }
    
    /** 设置头部信息,KEY统一使用小写 */
    public void setHeader(String key, String value)
    {
        headerMap.put(key.toLowerCase(), value);
    }
    
    @Override /** 获取请求头属性 */
    public String getHeader(String key)
    {
        return headerMap.get(key.toLowerCase());
    }
    
    @Override /** 获取请求头中的编码,如果未设置默认null */
    public String getCharacterEncodingHeader()
    {
        String contentType= getHeader(_CONTENT_TYPE_);
        if (Validates.isEmptyBlank(contentType))
            return null;
        
        contentType = contentType.toLowerCase();
        int i1 = contentType.indexOf("charset=");
        if (i1 == -1)
            return null;
        
        int i2 = contentType.indexOf(";", i1);
        if (i2 == -1)
            return contentType.substring(i1 + 8);
        else
            return contentType.substring(i1 + 8, i2).trim();
    }
    
    /** 获取请求内容长度 */
    public int getContentLength()
    {
        String sLen = getHeader(_CONTENT_LENGTH_);
        if (!Validates.isInteger(sLen))
            return 0;
        return Integer.parseInt(sLen);
    }

    /** 获取请求内容类型 */
    public String getContentType()
    {
        return getHeader(_CONTENT_TYPE_);
    }
    
    /** 获取请求要求的类型 */
    public String getMimeType()
    {
        return mimeType;
    }
    
    /** 判断是否表单提交 */
    public boolean isMimeForm()
    {
        return _APPLICATION_X_WWW_FORM_.equals(mimeType);
    }
    
    /** 判断是否文本请求 */
    public boolean isMimeTextPlain()
    {
        return _TEXT_PLAIN_.equals(mimeType);
    }
    
    /** 设置请求的编码格式 */
    public void setCharacterEncoding(String characterEncoding)
    {
        if (Validates.isEmptyBlank(characterEncoding))
            return;//不支持设置空白编码格式
        
        this.characterEncoding = characterEncoding;
    }

    /** 获取请求要求的编码,如果未设置默认UTF-8 */
    public String getCharacterEncoding()
    {
        return characterEncoding;
    }
    
    /** 获取协议格式 */
    public String getScheme()
    {
        String proxy = getHeader(_X_FORWARDED_PROTO_);
        return (Validates.isEmpty(proxy))?server.getScheme():proxy;
    }
    
    /** 获取HOST:PORT */
    public String getHostPort()
    {
        return getHeader(_HOST_);
    }
    
    /** 如果有PORT仅取HOST */
    public String getHostOnly()
    {
        String hostPort = getHostPort();
        if (Validates.isEmpty(hostPort))
            return null;
        
        int ind = hostPort.indexOf(":");
        if (ind == -1)
            return hostPort;
        else//去除:之间的空格
            return Strings.trim(hostPort.substring(0, ind));
    }
    
    /** 获取端口信息 */
    public int getPort()
    {
        String scheme = getScheme();
        int defaultPort = _HTTPS_.equalsIgnoreCase(scheme)?443:80;
        String hostPort = getHostPort();
        if (Validates.isEmpty(hostPort) || hostPort.indexOf(":") == -1)
            return defaultPort;
        
        int ind = hostPort.indexOf(":");
        return Ints.toInt(Strings.trim(hostPort.substring(ind+1)), defaultPort);
    }
    
    /** 获取域名端口和路径值 */
    public HttpDomainPortPath getDomainPortPath()
    {
        return domainPortPath;
    }
    
    /** 获取浏览器代理 */
    public String getUserAgent()
    {
        return getHeader(_USER_AGENT_);
    }
    
    /** 是否请求内容GZIP */
    public boolean isRequestGZip()
    {
        String contentEncoding = getHeader(_CONTENT_ENCODING_);
        return _ENCODING_GZIP_.equalsIgnoreCase(contentEncoding);
    }
    
    /** 是否响应支持GZIP */
    public boolean isResponseGZip()
    {
        String acceptEncoding = getHeader(_ACCEPT_ENCODING_);
        if (Validates.isEmpty(acceptEncoding) || acceptEncoding.indexOf(_ENCODING_GZIP_) == -1)
            return false;
        
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent) || userAgent.toLowerCase().indexOf("msie 6.0") > -1)
            return false;//IE6,有部分浏览器有问题可能不支持GZIP
        
        return true;
    }
    
    public String getXForwardedFor()
    {
        return x_Forwarded_For;
    }
    
    public void setCertificates(X509Certificate[] certs)
    {
        this.certs = certs;
    }
    
    public X509Certificate[] getCertificates()
    {
        return certs;
    }
    
    /** 是否是Websocket协议 */
    public boolean isWebSocket()
    {
        return _WEBSOCKET_.equalsIgnoreCase(getHeader(_UPGRADE_)) &&  Strings.contains(getHeader(_CONNECTION_).toLowerCase(), _UPGRADE_.toLowerCase());
    }
    
    /** 由转向时设置新的地址 */
    public void setPathInContext(String pathInContext)
    {
        pathInContext = Strings.addStartsWith(pathInContext, "/");
        
        if ("/".equals(pathInContext))
        {//如果是根地址,则加上可能的welcomeUrl
            if (Validates.isNotEmptyBlank(context.getWelcomeUrl()))
                pathInContext = context.getWelcomeUrl();
        }
        
        this.pathInContext = pathInContext;
    }
    
    /***********************************************************************/
    //在找到HttpContext时才有效
    /***********************************************************************/
    
    /** 获取HTTP连接 */
    public HttpConnection getConnection()
    {
        return conn;
    }
    
    /** 获取在上下文环境下的路径 */
    public String getPathInContext()
    {
        return pathInContext;
    }
    
    /***********************************************************************/
    //由远程调用处理器指定的权限路径
    /***********************************************************************/
    
    /** 获取远程调用的权限路径 */
    public String getPathInRMI()
    {
        return pathInRMI;
    }
    
    /** 设置资源文件下绝对路径 ,由context回调 */
    public void setPathInRMI(String pathInRMI)
    {
        this.pathInRMI = pathInRMI;
    }
    
    /***********************************************************************/
    //由ClassResourceHandler和FileResourceHandler处理的资源临时文件路径
    /***********************************************************************/
    
    /** 获取资源文件下绝对路径 */
    public String getPathOnResource()
    {
        return pathOnResource;
    }
    
    /** 设置资源文件下绝对路径 ,由context回调 */
    public void setPathOnResource(String pathOnResource)
    {
        this.pathOnResource = pathOnResource;
    }
    
    /***********************************************************************/
    //toString & destroy
    /***********************************************************************/
    
    public String toString()
    {
        StringBuilder strb = new StringBuilder();
        strb.append(statusLine).append(_BR_);
        if (headerMap != null)
        {
            for (Entry<String, String> entry : headerMap.entrySet())
            {
                strb.append(entry.getKey()).append(_COLON_).append(entry.getValue()).append(_BR_);
            }
            strb.append(_BR_);
        }
        return strb.toString();
    }
    
    private void destroy()
    {
        if (headerMap != null)
        {
            headerMap.clear();
            headerMap = null;
        }
        
        if (liner != null)
        {
            liner.clear();
            liner = null;
        }
        
        //引用置空
        statusLine = null;
        method = null;
        uri = null;
        version = null;
        x_Forwarded_For = null;
        mimeType = null;
        characterEncoding = null;
        
        conn = null;
        context = null;
        pathInContext = null;
        pathOnResource = null;
    }
}