本文主要是介绍手写一个民用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);} }
这篇关于手写一个民用Tomcat (07)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!