jhttpp2.Jhttpp2HTTPSession.java Source code

Java tutorial

Introduction

Here is the source code for jhttpp2.Jhttpp2HTTPSession.java

Source

package jhttpp2;

/* Written and copyright 2001-2003 Benjamin Kohl.
 * Distributed under the GNU General Public License; see the README file.
 * This code comes with NO WARRANTY.
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import jhttpp2.admin.Jhttpp2Admin;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * One HTTP connection
 * 
 * @file Jhttpp2HTTPSession.java
 * @author Benjamin Kohl
 */
public class Jhttpp2HTTPSession extends Thread {
    private static Log log = LogFactory.getLog(Jhttpp2HTTPSession.class);

    public static final int SC_OK = 0;
    public static final int SC_CONNECTING_TO_HOST = 1;
    public static final int SC_HOST_NOT_FOUND = 2;
    public static final int SC_URL_BLOCKED = 3;
    public static final int SC_CLIENT_ERROR = 4;
    public static final int SC_INTERNAL_SERVER_ERROR = 5;
    public static final int SC_NOT_SUPPORTED = 6;
    public static final int SC_REMOTE_DEBUG_MODE = 7;
    public static final int SC_CONNECTION_CLOSED = 8;
    public static final int SC_HTTP_OPTIONS_THIS = 9;
    public static final int SC_FILE_REQUEST = 10;
    public static final int SC_MOVED_PERMANENTLY = 11;
    public static final int SC_CONFIG_RQ = 12;

    private Jhttpp2Server server;

    /** downstream connections */
    private Socket client;
    private BufferedOutputStream out;
    private Jhttpp2ClientInputStream in;

    /** upstream connections */
    private Socket HTTP_Socket;
    private BufferedOutputStream HTTP_out;
    private Jhttpp2ServerInputStream HTTP_in;

    public Jhttpp2HTTPSession(Jhttpp2Server server, Socket client) {
        try {
            in = new Jhttpp2ClientInputStream(server, this, client.getInputStream());// ,true);
            out = new BufferedOutputStream(client.getOutputStream());
            this.server = server;
            this.client = client;
        } catch (IOException e_io) {
            try {
                client.close();
            } catch (IOException e_io2) {
                log.debug("Error while closing client (kinda expected)" + e_io);
            }
            log.warn("Error while creating IO-Streams: ", e_io);
            return;
        }
        start();
    }

    public Socket getLocalSocket() {
        return client;
    }

    public Socket getRemoteSocket() {
        return HTTP_Socket;
    }

    public boolean isTunnel() {
        return in.isTunnel();
    }

    public boolean notConnected() {
        return HTTP_Socket == null;
    }

    public void sendHeader(int a, boolean b) throws IOException {
        sendHeader(a);
        endHeader();
        out.flush();
    }

    public void sendHeader(int status, String content_type, long content_length) throws IOException {
        sendHeader(status);
        sendLine("Content-Length", String.valueOf(content_length));
        sendLine("Content-Type", content_type);
    }

    public void sendLine(String s) throws IOException {
        write(out, s + "\r\n");
    }

    public void sendLine(String header, String s) throws IOException {
        write(out, header + ": " + s + "\r\n");
    }

    public void endHeader() throws IOException {
        write(out, "\r\n");
    }

    public void run() {
        log.debug("begin http session");
        server.increaseNumConnections();
        try {
            handleRequest();
        } catch (IOException e_handleRequest) {
            log.debug(e_handleRequest.toString());
        } catch (Exception e) {
            log.warn("Error hanling request ", e);
        }
        try {
            // close downstream connections
            in.close(); // since 0.4.10b
            out.close();
            client.close();
            // close upstream connections (webserver or other proxy)
            if (!notConnected()) {
                HTTP_Socket.close();
                HTTP_out.close();
                HTTP_in.close();
            }
        } catch (IOException e_run) {
            System.out.println(e_run.getMessage());
        }
        server.decreaseNumConnections();
        log.debug("end http session");
    }

    /** sends a message to the user */
    public void sendErrorMSG(int a, String info) throws IOException {
        String statuscode = sendHeader(a);
        String localhost = "localhost";
        try {
            localhost = InetAddress.getLocalHost().getHostName() + ":" + server.port;
        } catch (UnknownHostException e_unknown_host) {
        }
        String msg = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><html>\r"
                + "<!-- jHTTPp2 error message --><HEAD>\r" + "<TITLE>" + statuscode + "</TITLE>\r"
                + "<link rel=\"stylesheet\" type=\"text/css\" href=\"http://" + localhost + "/style.css\"></HEAD>\r" // use css style sheet in htdocs
                + "<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#000080\" VLINK=\"#000080\" ALINK=\"#000080\">\r"
                + "<h2 class=\"headline\">HTTP " + statuscode + " </h2>\r" + "<HR size=\"4\">\r"
                + "<p class=\"i30\">Your request for the following URL failed:</p>"
                + "<p class=\"tiagtext\"><a href=\"" + in.getFullURL() + "\">" + in.getFullURL() + "</A> </p>\r"
                + "<P class=\"i25\">Reason: " + info + "</P>" + "<HR size=\"4\">\r"
                + "<p class=\"i25\"><A HREF=\"http://jhttp2.sourceforge.net/\">jHTTPp2</A> HTTP Proxy, Version "
                + server.getServerVersion() + " at " + localhost
                + "<br>Copyright &copy; 2001-2003 <A HREF=\"mailto:bkohl@users.sourceforge.net\">Benjamin Kohl</A></p>\r"
                + "<p class=\"i25\"><A HREF=\"http://" + localhost
                + "/\">jHTTPp2 local website</A> <A HREF=\"http://" + localhost + "/" + server.WEB_CONFIG_FILE
                + "\">Configuration</A></p>" + "</BODY></HTML>";
        sendLine("Content-Length", String.valueOf(msg.length()));
        sendLine("Content-Type", "text/html; charset=iso-8859-1");
        endHeader();
        write(out, msg);
        out.flush();
    }

    public String sendHeader(int a) throws IOException {
        String stat;
        switch (a) {
        case 200:
            stat = "200 OK";
            break;
        case 202:
            stat = "202 Accepted";
            break;
        case 300:
            stat = "300 Ambiguous";
            break;
        case 301:
            stat = "301 Moved Permanently";
            break;
        case 400:
            stat = "400 Bad Request";
            break;
        case 401:
            stat = "401 Denied";
            break;
        case 403:
            stat = "403 Forbidden";
            break;
        case 404:
            stat = "404 Not Found";
            break;
        case 405:
            stat = "405 Bad Method";
            break;
        case 413:
            stat = "413 Request Entity Too Large";
            break;
        case 415:
            stat = "415 Unsupported Media";
            break;
        case 501:
            stat = "501 Not Implemented";
            break;
        case 502:
            stat = "502 Bad Gateway";
            break;
        case 504:
            stat = "504 Gateway Timeout";
            break;
        case 505:
            stat = "505 HTTP Version Not Supported";
            break;
        default:
            stat = "500 Internal Server Error";
        }
        sendLine(server.getHttpVersion() + " " + stat);
        sendLine("Server", server.getServerIdentification());
        if (a == 501)
            sendLine("Allow", "GET, HEAD, POST, PUT, DELETE, CONNECT");
        sendLine("Cache-Control", "no-cache, must-revalidate");
        sendLine("Connection", "close");
        return stat;
    }

    /** the main routine, where it all happens */
    public void handleRequest() throws Exception {
        InetAddress remote_host;
        Jhttpp2Read remote_in = null;
        int remote_port;
        byte[] b = new byte[65536];
        int numread = in.read(b);

        while (true) { // with this loop we support persistent connections
            if (numread == -1) { // -1 signals an error
                if (in.getStatusCode() != SC_CONNECTING_TO_HOST) {
                    switch (in.getStatusCode()) {
                    case SC_CONNECTION_CLOSED:
                        break;
                    case SC_CLIENT_ERROR:
                        sendErrorMSG(400, "Your client sent a request that this proxy could not understand. ("
                                + in.getErrorDescription() + ")");
                        break;
                    case SC_HOST_NOT_FOUND:
                        sendErrorMSG(504,
                                "Host not found.<BR>jHTTPp2 was unable to resolve the hostname of this request. <BR>Perhaps the hostname was misspelled, the server is down or you have no connection to the internet.");
                        break;
                    case SC_INTERNAL_SERVER_ERROR:
                        sendErrorMSG(500, "Server Error! (" + in.getErrorDescription() + ")");
                        break;
                    case SC_NOT_SUPPORTED:
                        sendErrorMSG(501, "Your client used a HTTP method that this proxy doesn't support: ("
                                + in.getErrorDescription() + ")");
                        break;
                    case SC_URL_BLOCKED:
                        sendErrorMSG(403,
                                (in.getErrorDescription() != null && in.getErrorDescription().length() > 0
                                        ? in.getErrorDescription()
                                        : "The request for this URL was denied by the jHTTPp2 URL-Filter."));
                        break;
                    // case SC_REMOTE_DEBUG_MODE: remoteDebug(); break;
                    case SC_HTTP_OPTIONS_THIS:
                        sendHeader(200);
                        endHeader();
                        break;
                    case SC_FILE_REQUEST:
                        file_handler();
                        break;
                    case SC_CONFIG_RQ:
                        admin_handler(b);
                        break;
                    // case SC_HTTP_TRACE:
                    case SC_MOVED_PERMANENTLY:
                        sendHeader(301);
                        write(out, "Location: " + in.getErrorDescription() + "\r\n");
                        endHeader();
                        out.flush();
                    default:
                    }
                    break; // return from main loop.
                } else { // also an error because we are not connected (or to
                    // the wrong host)
                    // Creates a new connection to a remote host.
                    if (!notConnected()) {
                        try {
                            HTTP_Socket.close();
                        } catch (IOException e_close_socket) {
                        }
                    }
                    numread = in.getHeaderLength(); // get the header length
                    if (!server.use_proxy) {// sets up hostname and port
                        remote_host = in.getRemoteHost();
                        remote_port = in.remote_port;
                    } else {
                        remote_host = server.proxy;
                        remote_port = server.proxy_port;
                    }
                    // if (server.debug)server.writeLog("Connect: " +
                    // remote_host + ":" + remote_port);
                    try {
                        connect(remote_host, remote_port);
                    } catch (IOException e_connect) {
                        if (server.debug)
                            server.writeLog(e_connect.toString());
                        sendErrorMSG(502,
                                "Error while creating a TCP connecting to [" + remote_host.getHostName() + ":"
                                        + remote_port
                                        + "] <BR>The proxy server cannot connect to the given address or port ["
                                        + e_connect.toString() + "]");
                        break;
                    } catch (Exception e) {
                        server.writeLog(e.toString());
                        sendErrorMSG(500, "Error: " + e.toString());
                        break;
                    }
                    if (!in.isTunnel() || (in.isTunnel() && server.use_proxy)) { // no
                        // SSL-Tunnel
                        // or
                        // SSL-Tunnel
                        // with
                        // another
                        // remote
                        // proxy:
                        // simply
                        // forward
                        // the
                        // request
                        HTTP_out.write(b, 0, numread);
                        HTTP_out.flush();
                    } else { // SSL-Tunnel with "CONNECT": creates a tunnel
                        // connection with the server
                        sendLine(server.getHttpVersion() + " 200 Connection established");
                        sendLine("Proxy-Agent", server.getServerIdentification());
                        endHeader();
                        out.flush();
                    }
                    remote_in = new Jhttpp2Read(server, this, HTTP_in, out); // reads
                    // data
                    // from
                    // the
                    // remote
                    // server
                    server.addBytesWritten(numread);
                }
            }
            while (true) { // reads data from the client
                numread = in.read(b);
                // if (server.debug)server.writeLog("Jhttpp2HTTPSession: " +
                // numread + " Bytes read.");
                if (numread != -1) {
                    HTTP_out.write(b, 0, numread);
                    HTTP_out.flush();
                    server.addBytesWritten(numread);
                } else
                    break;
            } // end of inner loop
        } // end of main loop
        out.flush();
        if (!notConnected() && remote_in != null)
            remote_in.close(); // close Jhttpp2Read thread
        return;
    }

    /** connects to the given host and port */
    public void connect(InetAddress host, int port) throws IOException {
        HTTP_Socket = new Socket(host, port);
        HTTP_in = new Jhttpp2ServerInputStream(server, this, HTTP_Socket.getInputStream(), false);
        HTTP_out = new BufferedOutputStream(HTTP_Socket.getOutputStream());
    }

    /** converts an String into a Byte-Array to write it with the OutputStream */
    public void write(BufferedOutputStream o, String p) throws IOException {
        o.write(p.getBytes(), 0, p.length());
    }

    /**
     * Small webserver for local files in {app}/htdocs
     * 
     * @since 0.4.04
     */
    public void file_handler() throws IOException {
        if (!server.www_server) {
            sendErrorMSG(500, "The jHTTPp2 built-in WWW server module is disabled.");
            return;
        }
        String filename = in.url;
        if (filename.equals("/"))
            filename = "index.html"; // convert / to index.html
        else if (filename.startsWith("/"))
            filename = filename.substring(1);
        if (filename.endsWith("/"))
            filename += "index.html"; // add index.html, if ending with /
        File file = new File("htdocs/" + filename); // access only files in
        // "htdocs"
        if (!file.exists() || !file.canRead() // be sure that we can read the
        // file
                || filename.indexOf("..") != -1 // don't allow ".." !!!
                || file.isDirectory()) { // dont't read if it's a directory
            sendErrorMSG(404, "The requested file /" + filename + " was not found or the path is invalid.");
            return;
        }
        int pos = filename.lastIndexOf("."); // MIME type of the specified file
        String content_type = "text/plain"; // all unknown content types will be
        // marked as text/plain
        if (pos != -1) {
            String extension = filename.substring(pos + 1);
            if (extension.equalsIgnoreCase("htm") || (extension.equalsIgnoreCase("html")))
                content_type = "text/html; charset=iso-8859-1";
            else if (extension.equalsIgnoreCase("jpg") || (extension.equalsIgnoreCase("jpeg")))
                content_type = "image/jpeg";
            else if (extension.equalsIgnoreCase("gif"))
                content_type = "image/gif";
            else if (extension.equalsIgnoreCase("png"))
                content_type = "image/png";
            else if (extension.equalsIgnoreCase("css"))
                content_type = "text/css";
            else if (extension.equalsIgnoreCase("pdf"))
                content_type = "application/pdf";
            else if (extension.equalsIgnoreCase("ps") || extension.equalsIgnoreCase("eps"))
                content_type = "application/postscript";
            else if (extension.equalsIgnoreCase("xml"))
                content_type = "text/xml";
        }
        sendHeader(200, content_type, file.length());
        endHeader();
        BufferedInputStream file_in = new BufferedInputStream(new FileInputStream(file));
        byte[] buffer = new byte[4096];
        int a = file_in.read(buffer);
        while (a != -1) { // read until EOF
            out.write(buffer, 0, a);
            a = file_in.read(buffer);
        }
        out.flush();
        file_in.close(); // finished!
    }

    /**
     * @since 0.4.10b
     */
    public int getStatus() {
        return in.getStatusCode();
    }

    /**
     * @since 0.4.20a admin webpage
     */
    public void admin_handler(byte[] b) throws IOException {
        if (!server.webconfig) {
            sendErrorMSG(500, "The web-based configuration module is disabled.");
            return;
        }
        Jhttpp2Admin admin = null;
        String filename = in.url;
        if (in.post_data_len > 0) { // if the client used "POST" then append the
            // data to the filename
            filename = filename + "?" + new String(b, in.getHeaderLength() - in.post_data_len, in.post_data_len);
        }
        if (filename.startsWith("/"))
            filename = filename.substring(1);
        String adminpage = "";
        try {
            admin = new Jhttpp2Admin(filename, server);
            admin.WebAdmin();
            adminpage = admin.HTMLAdmin();
        } catch (Exception e) {
            e.printStackTrace();
            server.writeLog("Jhttpp2Admin Exception: " + e.getMessage());
        }
        int adminlen = adminpage.length();
        if (adminlen < 1) {
            sendErrorMSG(500, "Error Message from the Web-Admin modul: " + Jhttpp2Admin.error_msg);
        } else {
            sendHeader(200, "text/html", adminlen);
            endHeader();
            write(out, adminpage);
        }
        out.flush();
    }
}