首页 > 代码库 > 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;        }    }}
View Code

 

RequestHander类介绍