手写一个民用Tomcat (06)

我们这次是引入获取参数,比如你的GET 请求 或者post 请求 如何吧请求参数进行封装 成map 集合 。

先看下erquest。请求类里边改造

private void parseRequestLine()  这个方法 改造成 依据 ?进行分割处理因为
http://localhost:8080/servlet/com.yixin.HelloWorldServlet?name=jxd&age=18请求 要把参数拿出来 name=jxd&age=18

新增了protected void parseParameters()  解析参数方方法

新增了 private static void putMapEntry 存入参数的方法

这里post 的解析只给出application/x-www-form-urlencoded 格式的请求,至于我们常用的json 请求 后期找机会给出,因为相对简单,就是解析 json 格式 的数据 。

具体的实现逻辑如下(给出关键性代码):

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();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);} else {queryString = null;uri = new String(requestLine.uri, 0, requestLine.uriEnd);}}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 {headers.put(name, value);}}}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;}@Overridepublic String getMethod() {return new String(this.requestLine.method, 0, this.requestLine.methodEnd);}@Overridepublic Collection<Part> getParts() throws IOException, ServletException {return null;}@Overridepublic Part getPart(String s) throws IOException, ServletException {return null;}@Overridepublic <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {return null;}@Overridepublic Object getAttribute(String s) {return null;}@Overridepublic Enumeration<String> getAttributeNames() {return null;}@Overridepublic String getCharacterEncoding() {return null;}@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;}}

这个类里边我们增加了

//这段代码是测试用,可以获取的 请求参数 支持get 和post
Map<String, String[]> map = requestFacade.getParameterMap();

这段代码 可以运行 测试的main 方法进行测试 看一下 map 里边的数据。

public class JxdServletProcessor {public void process(JxdRequest request, JxdResponse response) {
//首先根据uri最后一个/号来定位,后面的字符串认为是servlet名字String uri = request.getUri();String servletName = uri.substring(uri.lastIndexOf("/") + 1);URLClassLoader loader = null;try {
// create a URLClassLoaderURL[] urls = new URL[1];URLStreamHandler streamHandler = null;File classPath = new File(JxdHttpServer.WEB_ROOT);String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();urls[0] = new URL(null, repository, streamHandler);loader = new URLClassLoader(urls);} catch (IOException e) {System.out.println(e.toString());}//由上面的URLClassLoader加载这个servletClass<?> servletClass = null;Servlet servlet = null;try {HttpRequestFacade requestFacade = new HttpRequestFacade(request);HttpResponseFacade responseFacade = new HttpResponseFacade(response);servletClass = loader.loadClass(servletName);response.setCharacterEncoding("UTF-8");response.addHeader(DefaultHeaders.CONTENT_TYPE_NAME,"text/html;charset=UTF-8");response.sendHeaders();//发送响应头//这段代码是测试用,可以获取的 请求参数 支持get 和postMap<String, String[]> map = requestFacade.getParameterMap();servlet = (Servlet) servletClass.newInstance();servlet.service(requestFacade, responseFacade);} catch (ClassNotFoundException | IOException e) {System.out.println(e.toString());} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}
}

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

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

相关文章

负载均衡原理|算法

负载均衡&#xff08;Load Balancing&#xff09;是一种计算机网络技术&#xff0c;其目的是将大量的并发请求或网络流量分散到多个服务器上&#xff0c;以此来提高服务的可用性、响应速度、以及系统的总体处理能力&#xff0c;同时减轻单个服务器的负担。负载均衡不仅能够避免…

太阳能路灯光伏板的朝向设计问题

题目&#xff1a;太阳能路灯光伏板的朝向设计问题 难度对标几乎每一年的国赛A题。 QQ群&#xff1a;592697532 公众号&#xff1a;川川菜鸟 文章目录 背景问题问题一问题二问题三 题目解读相关公式&#xff08;必备&#xff09;太阳辐射的计算光伏板接收的辐射光学效率大 气透…

OpenXR手部追踪实现详解

在虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;应用中&#xff0c;手部追踪技术是提高用户交互自然性的关键技术之一。本文将详细介绍如何使用OpenXR API实现手部追踪功能&#xff0c;包括系统属性的查询、手部追踪器的创建和手部关节的定位。 开…

Spring Cloud Gateway详细介绍以及实现动态路由

一. 简介 Spring Cloud Gateway This project provides a libraries for building an API Gateway on top of Spring WebFlux or Spring WebMVC. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to …

C++的线程

#include<iostream> #include<thread> #include<unistd.h> using namespace std; void myrun() {while(true){cout<<"I am a thread"<<endl;sleep(1);} } int main() {thread t(myrun);t.join();return 0; } 如果不添加-lpthread就会报…

基于ChatGPT打造安全脚本工具流程

前言 以前想要打造一款自己的工具&#xff0c;想法挺好实际上是难以实现&#xff0c;第一不懂代码的构造&#xff0c;只有一些工具脚本构造思路&#xff0c;第二总是像重复造轮子这种繁琐枯燥工作&#xff0c;抄抄改改搞不清楚逻辑&#xff0c;想打造一款符合自己工作的自定义的…

Day 25 组合(优化)216.组合总和III 17.电话号码的字母组合

组合&#xff08;优化&#xff09; 先给出组合问题的回溯部分代码&#xff1a; vector<vector<int>> result; // 存放符合条件结果的集合vector<int> path; // 用来存放符合条件结果void backtracking(int n, int k, int startIndex) {if (path.size() k) …

【opencv】dnn示例-person_reid.cpp 人员识别(ReID,Re-Identification)系统

ReID(Re-Identification&#xff0c;即对摄像机视野外的人进行再识别) 0030_c1_f0056923.jpg 0042_c5_f0068994.jpg 0056_c8_f0017063.jpg 以上为输出结果&#xff1a;result文件夹下 galleryLIst.txt queryList.txt 模型下载&#xff1a; https://github.com/ReID-Team/ReID_e…

简搜,一个安卓应用,用于扫描纸质书,把它变成可以用多个关键词搜索的电子书

下载连接&#xff1a;简搜scanner-app资源-CSDN文库 扫描纸质书&#xff0c;让它变成可以像百度搜索一样使用的电子书。 诸如自考、公务员考试考试中&#xff0c;需要大量刷题时&#xff0c;大部分知识点就在书中&#xff0c;但是要找到它&#xff0c;通常要花几分钟甚至个把…

OpenHarmony网络通信-socket-io

简介 socket.io是一个在客户端和服务器之间实现低延迟、双向和基于事件的通信的库。建立在 WebSocket 协议之上&#xff0c;并提供额外的保证&#xff0c;例如回退到 HTTP 长轮询或自动重新连接。 效果展示 下载安装 ohpm install ohos/socketio OpenHarmony ohpm 环境配置等更…

VulnHub靶机 DC-5 打靶 渗透测试详情过程

VulnHub靶机 DC-5 打靶 详细渗透测试过程 目录 VulnHub靶机 DC-5 打靶 详细渗透测试过程一、将靶机导入到虚拟机当中二、渗透流程主机发现端口扫描目录爆破文件包含getshell反弹shell提权 一、将靶机导入到虚拟机当中 靶机地址&#xff1a; https://download.vulnhub.com/dc/…

【云计算】云计算八股与云开发核心技术(虚拟化、分布式、容器化)

【云计算】云计算八股与云开发核心技术&#xff08;虚拟化、分布式、容器化&#xff09; 文章目录 一、什么是云计算&#xff1f;1、云计算的架构&#xff08;基础设施&#xff0c;平台&#xff0c;软件&#xff09;2、云计算的发展 二、如何做云计算开发&#xff1f;云计算的核…

量子时代加密安全与区块链应用的未来

量子时代加密安全与区块链应用的未来 现代密码学仍然是一门相对年轻的学科&#xff0c;但其历史却显示了一种重要的模式。大多数的发展都是基于几年甚至几十年前的研究。而这种缓慢的发展速度也是有原因的&#xff0c;就像药物和疫苗在进入市场之前需要经过多年的严格测试一样&…

K8s ingress-controller中nginx文件上传大小的限制

# 20、K8s ingress-controller中nginx文件上传大小的限制 问题&#xff1a;1.应用程序中上传文件文件出错&#xff0c;页面提示“您上传的文件太大了&#xff0c;请压缩图片后重试。” 2.通过F12 可以看到&#xff0c;后台提示 403错误&#xff0c;可以看到出错是由于nginx的限…

心学从0开始学习rust-十万个为什么篇章(持续更新篇章)

问答环节 1.const x 1;和let x 1有何区别呢&#xff0c;const申请的是全局变量所以会一直有效对吗&#xff1f; const 声明的常量具有全局作用域&#xff0c;但它们不能直接在函数内部声明。常量通常用于定义整个程序中使用的值&#xff0c;如配置常量或数学常量。 let 声明…

tcp网络编程——2

1.一个服务器只能有一个客户端连接&#xff08;下面代码&#xff09; ​​​​​​​tcp网络编程&#xff08;基础&#xff09;-CSDN博客 2.一个服务器可以有多个客户端连接&#xff08;多线程&#xff09; server端创建多个线程&#xff0c;每个线程与不同的client端建立连…

浅写个登录(无js文件)

全部代码如下&#xff0c;无需编写wxss文件&#xff0c;渲染都在style里面&#xff1a; <view style"height: 250rpx;width: 100%;"> <!-- 背景图片 --><view style"position: absolute; background-color: antiquewhite; height: 250rpx;width…

代码随想录-算法训练营day16【二叉树03:二叉树的最大深度、二叉树的最小深度、完全二叉树的节点个数】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第六章 二叉树part03今日内容&#xff1a; ● 104.二叉树的最大深度 559.n叉树的最大深度 ● 111.二叉树的最小深度 ● 222.完全二叉树的节点个数迭代法&#xff0c;大家可以直接过&#xff0c;二刷有精力的时候 再去…

IDEA中Docker相关操作的使用教程

一、引言 Docker作为当前最流行的容器化技术&#xff0c;极大地简化了应用的部署和管理。而IntelliJ IDEA作为一款强大的集成开发环境&#xff0c;也提供了对Docker的集成支持。本文将介绍如何在IDEA中配置和使用Docker&#xff0c;包括远程访问配置、服务连接、Dockerfile编写…

【C语言】冒泡排序算法详解

目录 一、算法原理二、算法分析时间复杂度空间复杂度稳定性 三、C语言实现四、Python实现 冒泡排序&#xff08;Bubble Sort&#xff09;是一种基础的排序算法。它重复地遍历要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。遍历数列…