首页 > 代码库 > RequestHander类介绍
RequestHander类介绍
RequestHander是一个抽象类,是一个线程。它封装了一个Socket。代码不难;
package org.simpleHTTPServer;import java.io.IOException;import java.net.Socket;/** * Handling Network Socket Requests. * This is "general-purpose" so far and could handle different protcols (i.e. it is NOT tied to HTTP). * * @author vorburger */abstract class RequestHandler implements Runnable { private final Socket socket; public RequestHandler(Socket socket) { this.socket = socket; } /** * Handle a request given a Socket - read stuff from it - answer stuff back, etc! * * @param socket * @throws IOException * @throws SimpleWebServerException */ protected abstract void handle(Socket socket) throws IOException, SimpleWebServerException; public void run() { try { handle(socket); } catch (Exception ex) { // TODO Real logging... (WARN here) ex.printStackTrace(); // Note: No re-throwing here! "The show must go on..." - servers doesn‘t die just because we had a problem with one request. } finally { try { // Should never be null, but let‘s be on the safe side anyway if ( socket != null ) { // Some options, faster & safer? socket.setSoLinger(false, 0); socket.shutdownInput(); socket.shutdownOutput(); socket.close(); } } catch (IOException e) { // Ignore... OK. } } }}
我们看到这种设计方法,可以称作是模板方法,整个逻辑框架已经写好,只有具体的handle()方法,可以通过重写不同的类重现不同的逻辑。
RequestHandlerHTTP10这个类,继承了RequestHandler类,重写了handle方法,在handler逻辑上写加上了对http协议的处理,但是依然是抽闲类,出现一个新的抽象方法
void handle(HTTPRequest request, HTTPResponse response)
protected abstract void handle(HTTPRequest request, HTTPResponse response) throws IOException;
//该方法中创建了httpRequest对象,和HttpResponse队形,然后调用handle(request,responce) @Override protected void handle(Socket socket) throws IOException, SimpleWebServerException {
//构建原始的HttpRequset信息 HTTPRequest request = this.getHTTPRequest(socket);
//构建响应信息 HTTPResponse response = new HTTPResponse(socket); // "The Date general-header field represents the date and time at which the message was originated" // TODO Profile/research: Is DateFormat initialization an expensive operation? Do this only once... DateFormat rfc1123_DateFormat = new SimpleDateFormat(RFC1123_DATE_PATTERN, Locale.US); rfc1123_DateFormat.setTimeZone(new SimpleTimeZone(0, "GMT")); String date = rfc1123_DateFormat.format(new Date()); response.setHeader(HTTPResponse.Header.Date, date); response.setHeader(HTTPResponse.Header.Server, "SimpleHTTPServer/1.0"); // This is Connection: close is probably not strictly neccessary as we are a HTTP/1.0 server so far here, but it can‘t work and seems to work well response.setHeader(HTTPResponse.Header.Connection, "close"); if (HTTPRequest.Version.HTTP11.toString().equals(request.getHTTPVersion())) { // Until HTTP/1.1 is properly implemented here, simply "force" (?) response to 1.0 (http://www.ietf.org/rfc/rfc2145.txt) // I‘m not 1000% sure if this is correct... but it seems to work well with HTTP/1.1 browsers... response.setHTTPVersion(HTTPRequest.Version.HTTP10); } else if (!HTTPRequest.Version.HTTP10.toString().equals(request.getHTTPVersion())) { throw new SimpleWebServerException("Don‘t know how to answer HTTP requests with this version header: " + request.getHTTPVersion()); } this.handle(request, response); System.out.println(socket.getInetAddress().getHostAddress()+" [" + new Date().toString() + "] " + request.getMethod() + " " + request.getHTTPVersion() + " " + request.getURI() + " " + response.getStatusCode()); // TODO HTTP/1.1 support, we probably don‘t want to close this response (ultimately, underlying socket) just yet and wait for more requests in this same Handler? response.close(); }
//从http请求信息中抽取信息,封装成HTTPRequest对象 private HTTPRequest getHTTPRequest(Socket socket) throws IOException, SimpleWebServerException { HTTPRequest r = new HTTPRequest(); InputStream is = socket.getInputStream(); // TODO Charset of IS? Try an URL with an Umlaut.. UTF-8? Reader reader = new InputStreamReader(is /* charset??? */); BufferedReader bufferedReader = new BufferedReader(reader/*, size??? Default is 8k - leave that for now */); String httpRequestLine = ""; // TODO Security: Use e.g. a custom BufferedReader subclass that limits characters per line and total lines to avoid DOS/exhaustion attacks.. (but take big file uploads via POST into account!) httpRequestLine = bufferedReader.readLine(); // This could throw a SocketTimeoutException, which will propagate to the caller, as it should. // If null, this also indicates a timeout occured, and we are not dealing with the request either... if (httpRequestLine == null) { throw new SimpleWebServerException("No (or not enough) data received (within timeout)"); } try { String[] httpRequestLineSplitArray = httpRequestLine.split(" "); r.method = httpRequestLineSplitArray[0]; r.URI = httpRequestLineSplitArray[1]; r.HTTPVersion = httpRequestLineSplitArray[2]; } catch (Exception ex) { throw new SimpleWebServerException("HTTP Request Line (1st line) invalid, should be ‘VERB URI VERSION‘ and not ‘" + httpRequestLine + "‘; see RFC 2616, Section 5", ex); } while (bufferedReader.ready()) { String line = bufferedReader.readLine(); if (line.length() == 0) { break; } int httpRequestHeaderKeySeparatorPos = line.indexOf(‘:‘); String httpRequestHeaderKey = line.substring(0, httpRequestHeaderKeySeparatorPos); String httpRequestHeaderValue = line.substring(httpRequestHeaderKeySeparatorPos + 1, line.length()); httpRequestHeaderValue = httpRequestHeaderValue.trim(); // RFC 2616 Section 4.2 r.headers.put(httpRequestHeaderKey, httpRequestHeaderValue); } // TODO Test if Header/Body delimiter code here works StringBuffer bodySB = new StringBuffer(1024); while (bufferedReader.ready()) { String line = ""; do { line = bufferedReader.readLine(); } while (line.length() == 0); bodySB.append(line); bodySB.append(‘\n‘); } r.body = bodySB.toString(); return r; }}
RequestHandlerStaticSite代码就很简单了
/** * Copyright 2006-2012 Michael Vorburger (http://www.vorburger.ch) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//******************************************************************************* * Copyright (c) 2006-2012 Michael Vorburger (http://www.vorburger.ch). * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/package org.simpleHTTPServer;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;import java.net.URI;import java.net.URISyntaxException;import javax.activation.MimetypesFileTypeMap;/** * Handle HTTP requests by serving fiels from the local filesystem. * Given a root directory, files corresponding to the URI are sent to the client. * * @author vorburger * @author romain */// TODO TestCase for RequestHandlerStaticSiteclass RequestHandlerStaticSite extends RequestHandlerHTTP10 { File siteRoot; public RequestHandlerStaticSite(Socket socket, File htDocsRootPath) { super(socket); siteRoot = htDocsRootPath; } protected void handleGet(HTTPRequest request, HTTPResponse response) throws IOException { // Note: The JDK URI class can do RFC 2396 encoding and decoding for us here... URI uri; try { uri = new URI(request.getURI()); } catch (URISyntaxException e) { response.setStatusCode(400); // 400 is Bad Request, seems a suitable answer for this case handleException(request, response, "URISyntaxException", e); return; } // This wouldn‘t handle %20-like encoding/decoding: String uri = request.getURI(); File file = new File(siteRoot, uri.getPath()); if (!file.exists()) { response.setStatusCode(404); // 404 is ‘Not Found‘, the correct answer for this case handleError(request, response, "File Not Found for requested URI ‘" + uri + "‘ "); return; } if (!file.canRead()) { response.setStatusCode(403); // 403 is ‘Forbidden‘, this seems appropriate here handleError(request, response, "Local file matched by requested URI is not readable"); // SECURITY Note: It‘s better not to show the full local path to the client, let‘s just log it on the server to help debugging return; } // TODO Security: Check that no request can read "outside" (above) the siteRoot... using getCanonicalPath() ? // (E.g. of the form http://localhost/../java/ch/vorburger/simplewebserver/RequestHandlerStaticSite.java if siteroot is src/htdocs-test) // TODO Implement modified-since stuff handling... something like: always send Last-Modified in response, and if request has a If-Modified-Since then check file with file.lastModified() and answer with code 304 if match (and Expires? Also not sure how exactly to handle If-Unmodified-Since request header) if (file.isFile()) { handleFile(file, response); } else if (file.isDirectory()) { handleDir(file, response); } else { handleError(request, response, "Content not file, not directory. We don‘t know how to handle it."); } } private static void handleFile(File file, HTTPResponse response) throws IOException { String filename = file.getName().toLowerCase(); String contentType = getContentType(filename); response.setContentType(contentType); long length = file.length(); response.setHeader(HTTPResponse.Header.ContentLength, Long.toString(length)); FileInputStream in; try { in = new FileInputStream(file); // TOD Charset conversion for text/* potentially needed? Do I need to use InputStreamReader(in, Charset/CharsetDecoder/String charsetName) here in some cases? OutputStream os = response.getOutputStream(); int c; while ((c = in.read()) != -1) { os.write(c); } in.close(); os.close(); } catch (FileNotFoundException ex) { throw new IOException("File " + file + " not found.", ex); } } private static String getContentType(String filename) { if (filename.endsWith(".js")) { return "application/javascript"; } else if (filename.endsWith(".css")) { return "text/css"; } else { return new MimetypesFileTypeMap().getContentType(filename); } } private void handleDir(File dir, HTTPResponse response) throws IOException { File indexFile = new File(dir.getAbsolutePath() + File.separator + "index.html"); if (indexFile.exists()) { redirect(indexFile, response); } else { StringBuilder builder = new StringBuilder("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\"><html> \n" + "<title>Directory listing for /</title>\n" + "<body>\n" + "<h2>Directory listing</h2>\n" + "<hr>\n" + "<ul>"); File[] files = dir.listFiles(); for (File file : files) { String link = "<li><a href=http://www.mamicode.com/"" + getWebPath(file) + "\">" + file.getName() + "<a/></li>\n"; builder.append(link); } builder.append("</ul>\n" + "<hr>\n" + "</body>\n" + "</html>"); String content = builder.toString(); response.setHeader(HTTPResponse.Header.ContentLength, Long.toString(content.length())); response.setContentType("text/html"); OutputStream os = response.getOutputStream(); os.write(content.getBytes("utf-8")); os.close(); } } private String getWebPath(File file) throws IOException { return file.getCanonicalPath().replace(siteRoot.getCanonicalPath(), ""); } private void redirect(File file, HTTPResponse response) throws IOException { response.setStatusCode(302); response.setHeader("Location", getWebPath(file)); } @Override protected void handle(HTTPRequest request, HTTPResponse response) throws IOException { try { if (!HTTPRequest.Method.GET.toString().equals(request.getMethod())) { response.setStatusCode(501); // 501 is "Not Implemented" return; } else { handleGet(request, response); } } catch (Exception ex) { handleException(request, response, "Server Error (Unexpected ‘" + ex.getMessage() + "‘ while handling request)", ex); } } private void handleError(HTTPRequest request, HTTPResponse response, String message) throws IOException { this.handleException(request, response, message, null); } private void handleException(HTTPRequest request, HTTPResponse response, String message, Exception ex) throws IOException { try { // If earlier code has already set a more precise HTTP error then // leave that, make it a generic 500 only if its still the default 200 if (response.getStatusCode() == 200) { response.setStatusCode(500); } PrintWriter pw; response.setContentType("text/html"); pw = response.getPrintWriter(); pw.println("<html><head><title>Server Error</title></head><body><h1>Server Error</h1><p>"); pw.println(message); pw.println("</p><pre>"); if (ex != null) { ex.printStackTrace(pw); } pw.println("</pre></body></html>"); } catch (IllegalStateException e) { // Oh, too late to getPrintWriter()? Well... log it but otherwise // ignore it; at least the setStatusCode() worked if we‘re here. System.out.println("Can‘t send stack trace to client because OutputStream was already open for something else: " + e.toString()); // TODO Real logging... System.out.println("Stack trace of where the IllegalStateException occured:"); e.printStackTrace(); return; } }}
RequestHander类介绍
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。