手写一个民用Tomcat (07)

继续我们的Tomcat ,我们完成了 参数封装成map,下面我们处理,Cookie 和session

我们先引入两个类Session,和SessionFacade(也是门面模式)

public class JxdSession implements HttpSession {private String sessionid;private long creationTime;private boolean valid;private Map<String,Object> attributes = new ConcurrentHashMap<>();@Overridepublic long getCreationTime() {return this.creationTime;}@Overridepublic String getId() {return this.sessionid;}@Overridepublic long getLastAccessedTime() {return 0;}@Overridepublic ServletContext getServletContext() {return null;}@Overridepublic void setMaxInactiveInterval(int i) {}@Overridepublic int getMaxInactiveInterval() {return 0;}@Overridepublic HttpSessionContext getSessionContext() {return null;}@Overridepublic Object getAttribute(String s) {return this.attributes.get(s);}@Overridepublic Object getValue(String s) {return this.attributes.get(s);}@Overridepublic Enumeration<String> getAttributeNames() {return Collections.enumeration(this.attributes.keySet());}@Overridepublic String[] getValueNames() {return new String[0];}@Overridepublic void setAttribute(String name, Object value) {this.attributes.put(name, value);}@Overridepublic void putValue(String name, Object value) {this.attributes.put(name, value);}@Overridepublic void removeAttribute(String name) {this.attributes.remove(name);}@Overridepublic void removeValue(String s) {}@Overridepublic void invalidate() {this.valid = false;}@Overridepublic boolean isNew() {return false;}public void setCreationTime(long creationTime) {this.creationTime = creationTime;}public boolean isValid() {return valid;}public void setValid(boolean valid) {this.valid = valid;}public void setId(String sessionid) {this.sessionid = sessionid;}
}

public class SessionFacade implements HttpSession{private HttpSession session;public SessionFacade(HttpSession session) {this.session = session;}@Overridepublic long getCreationTime() {return session.getCreationTime();}@Overridepublic String getId() {return session.getId();}@Overridepublic long getLastAccessedTime() {return session.getLastAccessedTime();}@Overridepublic ServletContext getServletContext() {return session.getServletContext();}@Overridepublic void setMaxInactiveInterval(int interval) {session.setMaxInactiveInterval(interval);}@Overridepublic int getMaxInactiveInterval() {return session.getMaxInactiveInterval();}@Overridepublic HttpSessionContext getSessionContext() {return session.getSessionContext();}@Overridepublic Object getAttribute(String name) {return session.getAttribute(name);}@Overridepublic Object getValue(String name) {return session.getValue(name);}@Overridepublic Enumeration<String> getAttributeNames() {return session.getAttributeNames();}@Overridepublic String[] getValueNames() {return session.getValueNames();}@Overridepublic void setAttribute(String name, Object value) {session.setAttribute(name, value);}@Overridepublic void putValue(String name, Object value) {session.putValue(name, value);}@Overridepublic void removeAttribute(String name) {session.removeAttribute(name);}@Overridepublic void removeValue(String name) {session.removeValue(name);}@Overridepublic void invalidate() {session.invalidate();}@Overridepublic boolean isNew() {return session.isNew();}
}

下面开始解析,在JxdRequest 完整的实现方法,注意:parseCookieHeaderTow这个方法是我用自己方式去写的 我写完以后看源码,发现 确实 还是源码写的比较好,你们可以参考一下。

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.Principal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;public class JxdRequest implements HttpServletRequest {private InputStream input;private SocketInputStream sis;private String uri;InetAddress address;int port;protected HashMap<String, String> headers = new HashMap<>();protected Map<String, String[]> parameters = new ConcurrentHashMap<>();HttpRequestLine requestLine = new HttpRequestLine();String sessionid;SessionFacade sessionFacade;Cookie[] cookies;HttpSession session;private boolean parsed = false;private String queryString;public void parse(Socket socket) {try {input = socket.getInputStream();this.sis = new SocketInputStream(this.input, 2048);parseConnection(socket);this.sis.readRequestLine(requestLine);parseRequestLine();//解析数据parseHeaders();} catch (IOException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}private void parseRequestLine() {int question = requestLine.indexOf("?");if (question >= 0) {queryString = new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1);uri = new String(requestLine.uri, 0, question);//处理参数串中带有jsessionid的情况int sessionIndex = uri.indexOf(DefaultHeaders.JSESSIONID_NAME);if (sessionIndex >= 0) {sessionid = uri.substring(sessionIndex + DefaultHeaders.JSESSIONID_NAME.length());uri = uri.substring(0, sessionIndex);}} else {queryString = null;uri = new String(requestLine.uri, 0, requestLine.uriEnd);//因为post 请求又是会带sessionidint sessionIndex = uri.indexOf(DefaultHeaders.JSESSIONID_NAME);if (sessionIndex >= 0) {sessionid = uri.substring(sessionIndex + DefaultHeaders.JSESSIONID_NAME.length());uri = uri.substring(0, sessionIndex);}}}private void parseConnection(Socket socket) {address = socket.getInetAddress();port = socket.getPort();}private void parseHeaders() throws IOException, ServletException {while (true) {HttpHeader header = new HttpHeader();sis.readHeader(header);//表示读取完毕if (header.nameEnd == 0) {if (header.valueEnd == 0) {return;} else {throw new ServletException("httpProcessor.parseHeaders.colon");}}String name = new String(header.name, 0, header.nameEnd);String value = new String(header.value, 0, header.valueEnd);// 设置相应的请求头if (name.equals(DefaultHeaders.ACCEPT_LANGUAGE_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.CONTENT_LENGTH_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.CONTENT_TYPE_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.HOST_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.CONNECTION_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.TRANSFER_ENCODING_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.COOKIE_NAME)) {headers.put(name, value);//处理cookie和sessionCookie[] cookiearr = parseCookieHeader(value);//parseCookieHeaderTow(0,value.toCharArray());this.cookies = cookiearr;for (int i = 0; i < cookies.length; i++) {if (cookies[i].getName().equals("jsessionid")) {this.sessionid = cookies[i].getValue();}}} else {headers.put(name, value);}}}/*** 这个方式是cookieValue = sessionid=123;username=jxd* 以;分成两节 每一节依次进行解析 =号前变得是key 后边的是value* 然后把后一节的数据覆盖到cookieValue=username=jxd* 继续解析*/public Cookie[] parseCookieHeader(String cookieValue) {if ((cookieValue == null) || (cookieValue.length() < 1)) return (new Cookie[0]);ArrayList<Cookie> cookieal = new ArrayList<>();while (cookieValue.length() > 0) {int semicolon = cookieValue.indexOf(';');if (semicolon < 0)semicolon = cookieValue.length();if (semicolon == 0)break;String token = cookieValue.substring(0, semicolon);if (semicolon < cookieValue.length())cookieValue = cookieValue.substring(semicolon + 1);elsecookieValue = "";try {int equalsIndex = token.indexOf('=');if (equalsIndex > 0) {String name = token.substring(0, equalsIndex).trim();String value = token.substring(equalsIndex + 1).trim();cookieal.add(new Cookie(name, value));}} catch (Throwable e) {}}return ((Cookie[]) cookieal.toArray(new Cookie[cookieal.size()]));}/***这个方法是 我自己想到的另一种解析方法,和tomcat 比起来确实 有点差距**/private static void parseCookieHeaderTow(int i,char[] strArr){char[] key = new char[strArr.length];char[] value = new char[strArr.length];int keyindex =0;for (; strArr[i] != '=' ; i++) {key[keyindex++] = strArr[i];}int valueindex =0;i++;//跳过一位for ( ; i<strArr.length&&strArr[i] != ';' ; i++) {value[ valueindex++] = strArr[i];}System.out.println(new String(key,0,keyindex)+" key");System.out.println(new String(value,0,valueindex)+" value");if(i< strArr.length){parseCookieHeaderTow(++i,strArr);}}protected void parseParameters() {String encoding = getCharacterEncoding();System.out.println(encoding);if (encoding == null) {encoding = "ISO-8859-1";}String qString = getQueryString();System.out.println("getQueryString:" + qString);if (qString != null) {byte[] bytes;try {bytes = qString.getBytes(encoding);parseParameters(this.parameters, bytes, encoding);} catch (UnsupportedEncodingException e) {e.printStackTrace();;}}String contentType = getContentType();if (contentType == null)contentType = "";int semicolon = contentType.indexOf(';');if (semicolon >= 0) {contentType = contentType.substring(0, semicolon).trim();} else {contentType = contentType.trim();}if ("POST".equals(getMethod()) && (getContentLength() > 0) && "application/x-www-form-urlencoded".equals(contentType)) {try {int max = getContentLength();int len = 0;byte buf[] = new byte[getContentLength()];ServletInputStream is = getInputStream();while (len < max) {int next = is.read(buf, len, max - len);if (next < 0) {break;}len += next;}is.close();if (len < max) {throw new RuntimeException("Content length mismatch");}parseParameters(this.parameters, buf, encoding);} catch (UnsupportedEncodingException ue) {} catch (IOException e) {throw new RuntimeException("Content read fail");}}}/*** parseParameters 这个方法 举例 例如data name=jxd&age=18* 他会从0位置遍历到最后一位 同时设置两个指针一个ix 一个ox* ix 就是从char 数组0位开始遍历到最后* ox 是一个查找指针 ,当遇到 = 或者&时候,进行取舍,=前边表示key ,& 前边表示value 如果没有& 表示结尾* 要注意一个细节 当遇到 =或者& 时候会把ox 赋值0 ,但是为啥要    default: data[ox++] = c;* ,当我们遇到第一个=的时候 下一个是value=jxd 那么之前那个key(name) 就没有用了,因为已经赋值到map里边了 ,所以读取* jxd 时候覆盖掉前边的nam,然后 ox指针因为是从0开始 等遇到jxd后变边的&时候* 照样能把value=jxd 取出来 这样 一个数组 就能完成了,虽然data 原数组被改变 这样看似不太好但是,但是节省空间 不然你就要2个数组才能完成,一个取值一个* 放值 不得不说设计的很巧妙 。*/public void parseParameters(Map<String, String[]> map, byte[] data, String encoding)throws UnsupportedEncodingException {if (parsed)return;System.out.println(data);if (data != null && data.length > 0) {int pos = 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 '=':key = new String(data, 0, ox, encoding);ox = 0;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);}}parsed = true;}private 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;}/*** 这个方式是 存入map集合 因为有的value值对应多个key 所以是数组形式存储value*/private static void putMapEntry(Map<String, String[]> 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);}public String getUri() {return uri;}public String getSessionId() {return this.sessionid;}//如果有存在的session,直接返回,如果没有,创建一个新的session@Overridepublic HttpSession getSession(boolean create) {if (sessionFacade != null)return sessionFacade;if (sessionid != null) {session = JxdHttpConnector.sessions.get(sessionid);if (session != null) {sessionFacade = new SessionFacade(session);return sessionFacade;} else {session = JxdHttpConnector.createSession();sessionFacade = new SessionFacade(session);return sessionFacade;}} else {session = JxdHttpConnector.createSession();sessionFacade = new SessionFacade(session);sessionid = session.getId();return sessionFacade;}}@Overridepublic String getAuthType() {return null;}@Overridepublic Cookie[] getCookies() {return this.cookies;}@Overridepublic long getDateHeader(String s) {return 0;}@Overridepublic String getHeader(String s) {return null;}@Overridepublic Enumeration<String> getHeaders(String s) {return null;}@Overridepublic Enumeration<String> getHeaderNames() {return null;}@Overridepublic int getIntHeader(String s) {return 0;}@Overridepublic String getMethod() {return new String(this.requestLine.method, 0, this.requestLine.methodEnd);}@Overridepublic String getPathInfo() {return null;}@Overridepublic String getPathTranslated() {return null;}@Overridepublic String getContextPath() {return null;}@Overridepublic String getQueryString() {return this.queryString;}@Overridepublic void setCharacterEncoding(String s) throws UnsupportedEncodingException {}@Overridepublic int getContentLength() {return Integer.parseInt(headers.get(DefaultHeaders.CONTENT_LENGTH_NAME));}@Overridepublic long getContentLengthLong() {return 0;}@Overridepublic String getContentType() {return headers.get(DefaultHeaders.CONTENT_TYPE_NAME);}@Overridepublic ServletInputStream getInputStream() throws IOException {return this.sis;}@Overridepublic String getParameter(String name) {parseParameters();String values[] = parameters.get(name);if (values != null)return (values[0]);elsereturn (null);}@Overridepublic Enumeration<String> getParameterNames() {parseParameters();return (Collections.enumeration(parameters.keySet()));}@Overridepublic String[] getParameterValues(String name) {parseParameters();String values[] = (String[]) parameters.get(name);if (values != null)return (values);elsereturn null;}@Overridepublic Map<String, String[]> getParameterMap() {parseParameters();return (this.parameters);}public static void main(String[] args) {String str = "sessionid=123;username=jxd";char[] strArr = str.toCharArray();parseCookieHeaderTow(0,strArr);}
}
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/4108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免费简单好用的内网穿透工具(ngrok、natapp),微信回调地址配置

B站视频地址 文章目录 Natapp1、登录注册账号、下载软件2、使用2-1、购买隧道、查看token2-2、端口穿透 Ngrok1、登录注册账号、下载软件2、使用2-1、获取并设置 token2-2、使用 3、隧道 微信回调配置1、注册测试公众号2、回调代码3、回调配置 在一些特殊的场景下&#xff0c;需…

【模型渲染】前端如何让glb模型转3dtiles

发现了一个新插件&#xff0c;3D Tiles Tools&#xff0c;CesiumGS 出品&#xff0c;新鲜热乎&#xff08;当前写这篇文章的时候&#xff0c;版本是v 0.4.1&#xff09;&#xff0c;所以&#xff0c;有些功能还不够使用。这里是我当前版本发现的问题&#xff0c;例如&#xff1…

多种方法求1+12+123+1234……

有网友出了一道题&#xff1a; 从键盘输入一个小于10的正整数n&#xff0c;计算1121231234……&#xff0c;即前n项之和。 第一眼看到题目&#xff0c;直觉告诉我必须使用嵌套的两个for循环&#xff0c;里面的循环生成每一项&#xff0c;外面的循环求和。错误的方向和思路让我…

基于RBF-PID控制器的风力发电系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于RBF-PID控制器的风力发电系统simulink建模与仿真,对比PID控制器和RBF-PID控制器的控制结果。 2.系统仿真结果 3.核心程序与模型 版本&#xff1a;MATLAB2022a 0050 4.系…

数据结构中的串(String):概念、操作和实际应用

目录 一.引言 二.串的定义 三. 串的抽象数据类型&#xff08;ADT&#xff09; 四. 串的存储结构 顺序存储结构 链式存储结构 五. 串模式的存储算法 KMP算法&#xff08;Knuth-Morris-Pratt算法&#xff09; 2.Brute-Force&#xff08;暴力匹配&#xff09;算法 3.Boy…

Unity进阶之ScriptableObject

目录 ScriptableObject 概述ScriptableObject数据文件的创建数据文件的使用非持久数据让其真正意义上的持久ScriptableObject的应用配置数据复用数据数据带来的多态行为单例模式化的获取数据 ScriptableObject 概述 ScriptableObject是什么 ScriptableObject是Unity提供的一个…

有没有电脑桌面监控软件|十大电脑屏幕监控软件超全盘点!

电脑桌面监控软件已经成为许多领域不可或缺的工具。 无论是企业为了保障数据安全和提高工作效率&#xff0c;还是家长为了监督孩子的学习&#xff0c;甚至是个人为了记录电脑使用行为&#xff0c;都需要这类软件的支持。 本文将对市面上十大电脑屏幕监控软件进行超全盘点&…

智能文案生成器,文案生成改写很强大

在当今数字化时代&#xff0c;随着人工智能的迅猛发展&#xff0c;智能文案生成器正逐渐成为营销和创作领域的一大利器。这些智能工具不仅能够快速生成文案&#xff0c;还能够进行文案改写&#xff0c;使得文案生成的过程更加高效、便捷。正是在这样的背景下&#xff0c;智能文…

CAT:contig稳健物种分类

安装 mamba create -n CAT python3.10 diamond prodigal cd SoftWare git clone https://github.com/MGXlab/CAT_pack chmod 755 给权限 自己构建数据库 names.dmp nodes.dmp文件可以在Kraken2的文件里面找到 Kraken2Bracken&#xff1a;宏基因组物种注释_kracken2配合bracke…

MySQL之binlog归档日志

binlog&#xff08;二进制归档日志&#xff09; binlog 二进制日志记录保存所有执行过的修改操作语句&#xff0c;不保存查询操作。如果 MySQL 服务意外停止&#xff0c;可通过二进制日志文件排查&#xff0c;用户操作或表结构操作&#xff0c;从而来恢复数据库数据。启动 bin…

Java 线程的几种状态

一、 线程的状态 状态是针对当前的线程调度的情况来描述的。因为线程是调度的基本单位&#xff0c;所以状态更应该是线程的属性。在Java中线程的状态一共有六种&#xff1a; 1. 初始(NEW)&#xff1a;新创建了一个线程对象&#xff0c;但还没有调用start()方法。 2. 运行(RUNN…

动静态库以及动态链接

文章目录 静态库制作静态库如何使用静态库 动态库动态库的制作动态库的使用动态链接 库是给别人用的&#xff0c;所以库中一定不存在main函数。库一般会有lib前缀和后缀&#xff0c;去掉前缀和后缀才是库名。 静态库 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译…

C++|对象与const

目录 常对象 常对象的声明 性质 数据成员不能被修改 常对象不能调用非const成员函数 const型成员函数 常对象成员 常数据成员 常成员函数 注意 总结 const型数据成员 const型成员函数 常对象 指针与const 指向对象的常指针 应用场景 指向常对象的指针 指向常…

Swagger3.0(Springdoc)日常使用记录

文章目录 前言一、默认地址二、注解OperationTag 三、SpringBoot基础配置四、Swagger导入apifox五、Swagger其他配置六 knife4j 参考文章 前言 本文并不是Swagger的使用教程&#xff0c;只是记录一下本人的操作&#xff0c;感兴趣的可以看下 一、默认地址 http://localhost:…

【1731】jsp 房租跟踪监控管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 房租跟踪监控管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysq…

Java 基础常见面试题整理

目录 1、java的基本数据类型有哪些&#xff1f;2、java为什么要有包装类型&#xff1f;3、String a "123" 和 String a new String("123") 区别&#xff1f;4、String、StringBuilder和StringBuffer的区别&#xff1f;5、如何理解面向对象和面向过程&…

浅谈叉车车载电脑的市场现状

叉车的起源 叉车源于美国&#xff0c;兴于日本&#xff0c;虽然中国起步较晚&#xff0c;但是近些年来发展迅速。叉车又称叉式装载车&#xff0c;是对于成件托盘类货物进行装卸、堆垛和短距离运输&#xff0c;实现重物搬运作业的轮式工业车辆。 叉车的分类 叉车分为以上六大类…

CSS学习(选择器、盒子模型)

1、CSS了解 CSS&#xff1a;层叠样式表&#xff0c;一种标记语言&#xff0c;用于给HTML结构设置样式。 样式&#xff1a;文字大小、背景颜色等 p标签内不能嵌套标题标签。 px是相对于分辨率而言的&#xff0c; em是相对于浏览器的默认字体&#xff0c; rem是相对于HTML根元…

刷题日记 ---- 顺序表与链表相关经典算法题(C语言版)

目录 1. 移除元素2. 合并两个有序数组3. 移除链表元素4. 反转链表5. 合并两个有序链表6. 链表的中间结点7. 环形链表的约瑟夫问题8. 分割链表总结 正文开始 1. 移除元素 题目链接: 移除元素 题目描述: 思路历程: 题目明确要求, 不能使用额外的数组空间, 也就是说不可以创建…

echarts之事件交互

ECharts 是一个优秀的开源可视化库&#xff0c;支持丰富的图表类型和交互功能。其中&#xff0c;事件交互是 ECharts 中非常重要的一部分&#xff0c;可以实现用户与图表的互动&#xff0c;比如点击、鼠标悬停等操作。下面我将为你介绍如何在 ECharts 中实现事件交互&#xff0…