Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/netscape/server/applet/HttpApplet.java

/*
 * @(#)HttpApplet.java	1.4 95/07/31
 * 
 * Copyright (c) 1995 Netscape Communications Corporation. All Rights Reserved.
 * 
 */

package netscape.server.applet;

import netscape.net.*;
import java.net.*;
import java.io.*;
import java.util.Hashtable;

public class HttpApplet extends ServerApplet {

    ////////////////////////////////////////////////////////////////////////////
    // Http-Specific Accessors
    ////////////////////////////////////////////////////////////////////////////

    public String getMethod() {
	return getRequestProperty("method");
    }

    URL requestURI;

    public URL getURI() throws MalformedURLException {
	if (requestURI == null)
	    requestURI = new URL(getURL(), getRequestProperty("uri"));
	return requestURI;
    }

    public String getProtocol() {
	return getRequestProperty("protocol");
    }

    public String getQuery() {
	return getRequestProperty("query");
    }

    public String getPath() {
	return getServerProperty("path");
    }

    public void setContentType(String type) {
	setResponseProperty("content-type", type);
    }

    URL serverURL;

    public URL getURL() throws MalformedURLException {
	if (serverURL == null)
	    serverURL = new URL(getServerProperty("url"));
	return serverURL;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    // Response Methods
    ////////////////////////////////////////////////////////////////////////////

    /**
     * Start the HTTP response to the request. This function returns 
     * NOACTION if the request was a HEAD request and thus no
     * body should be sent. Otherwise, it returns PROCEED.
     * Any headers contained in srvhdrs will be sent, and the
     * status code set with http::status will be sent.
     */
    public native int startResponse() throws IOException;

    /**
     * Start the HTTP response to the request. This function returns 
     * false if the request was a HEAD request and thus no
     * body should be sent. Otherwise, it returns true.
     * Any headers contained in srvhdrs will be sent, and the
     * status code set with http::status will be sent.
     */
    public boolean returnNormalResponse(String contentType, File statFile)
		throws IOException {
	setContentType(contentType);
	if (statFile != null)
	    setFileInfo(statFile);
	status(OK);
	responseStarted = true;
	return startResponse() != NOACTION;
    }

    /**
     * Start the HTTP response to the request. This function returns 
     * false if the request was a HEAD request and thus no
     * body should be sent. Otherwise, it returns true.
     * Any headers contained in srvhdrs will be sent, and the
     * status code set with http::status will be sent.
     */
    public boolean returnNormalResponse(String contentType)
		throws IOException {
	return returnNormalResponse(contentType, null);
    }

    /**
     * Start the HTTP response to the request returning the contents
     * of a file.
     */
    public void returnFile(String contentType, File file) throws IOException {
	if (returnNormalResponse(contentType, file)) {
	    FileInputStream fin = new FileInputStream(file);
	    PrintStream out = getOutputStream();
	    int n;
	    byte buf[] = new byte[1024];
	    while ((n = fin.read(buf)) != -1) {
		out.write(buf, 0, n);
	    }
	}
    }

    /**
     * Start the HTTP response to the request returning the contents
     * of a file.
     */
    public void returnFile(File file) throws IOException {
	returnFile(netscape.net.URLConnection.guessContentTypeFromName(file.getName()),
		   file);
    }

    /**
     * Start the HTTP response to the request. This function returns 
     * false if the request was a HEAD request and thus no
     * body should be sent. Otherwise, it returns true.
     * Any headers contained in srvhdrs will be sent, and the
     * status code set with http::status will be sent.
     */
    public boolean returnErrorResponse(String contentType, int status,
				       String reason)
		throws IOException {
	setContentType(contentType);
	status(status, reason);
	responseStarted = true;
	return startResponse() != NOACTION;
    }

    /**
     * Start the HTTP response to the request. This function returns 
     * false if the request was a HEAD request and thus no
     * body should be sent. Otherwise, it returns true.
     * Any headers contained in srvhdrs will be sent, and the
     * status code set with http::status will be sent.
     */
    public boolean returnErrorResponse(String contentType, int status)
		throws IOException {
	return returnErrorResponse(contentType, status, null);
    }

    /**
     * Status sets the status to the given integer code. The reason string
     * will be used to describe the status.
     */
    public native void status(int n, String reason);

    /**
     * Status sets the status to the given integer code. The reason string
     * will be a default internal string for the given code.
     */
    public void status(int n) {
        status(n, null);
    }

    /**
     * Set HTTP response headers according to the given file. If the client
     * gave a conditional request, that is, only send the object if it has
     * changed since a certain date, this function will return 
     * ABORTED to indicate that the calling function should not
     * proceed with the request.
     */
    public native int setFileInfo(File fi);

    /**
     * Given a URI or virtual path, translate it into a filesystem path
     * using the server's name translation facilities. Returns null
     * if no mapping could be generated.
     */
    public native String translateURI(String uri);

    /**
     * This generates a full URL from a prefix and suffix string. The 
     * prefix and suffix strings are concatenated to form the URL after the
     * http://host:port part. Either prefix or suffix can be null.
     */
    public static String uri2url(String prefix, String suffix) {
        StringBuffer ret = new StringBuffer("http");
	Server server = getServer();
        int port = server.getListeningPort();

        if (server.securityActive())
            ret.append("s");
        ret.append(":" + server.getAddress().getHostName());
        if (port != DEFAULT_PORT)
            ret.append(":" + port);
        if (prefix == null)
            ret.append(prefix);
        if (suffix == null)
            ret.append(suffix);
        return ret.toString();
    }

    /**
     * Takes the form data from this request, and converts it into an array
     * of decoded name=value strings. If the request does not have form
     * data attached, or any of the parameters the client gave are not sane,
     * an IOException is thrown. 
     */
    public Hashtable getFormData() throws IOException {
        String method = getMethod();
        String formdata;
        if (method.equals("GET") || method.equals("HEAD")) {
            formdata = getQuery();
            if (formdata == null)
                throw new IOException("missing query string");
        }
        else if (method.equals("POST")) {
            String t;
            int cl;
            byte[] b;

            /* Get content length, and sanity check it */
            t = getHeader("content-length");
            if (t == null)
                throw new IOException("no content length");
            try {
                cl = Integer.parseInt(t, 10);
            }
            catch (NumberFormatException e) {
                cl = -1;
            }
            if ((cl < 0) || (cl > MAXFORMLENGTH))
                throw new IOException("illegal content length");

            /* Get content type, and sanity check it. */
            t = getHeader("content-type");
            if ((t == null) || (!t.equals(FORMTYPE)))
                throw new IOException("illegal content type");

            b = new byte[cl];
	    DataInputStream din =
		new DataInputStream(getClientSocket().getInputStream());
	    din.readFully(b);
            formdata = new String(b, 0, 0, b.length);
        }
        else
            throw new IOException("unknown method");

        return URIUtil.splitFormData(formdata);
    }

    public static final int OK = 200;
    public static final int NO_RESPONSE = 204;
    public static final int REDIRECT = 302;
    public static final int NOT_MODIFIED = 304;
    public static final int BAD_REQUEST = 400;
    public static final int UNAUTHORIZED = 401;
    public static final int FORBIDDEN = 403;
    public static final int NOT_FOUND = 404;
    public static final int SERVER_ERROR = 500;
    public static final int NOT_IMPLEMENTED = 501;

    private static final int DEFAULT_PORT = 80;
    private static final int MAXFORMLENGTH = 1048576;
    private static final String FORMTYPE = "application/x-www-form-urlencoded";
    
    ////////////////////////////////////////////////////////////////////////////
    // Private Stuff
    ////////////////////////////////////////////////////////////////////////////

    /**
     * Called by the system. 
     */
    protected int handleRequest(int rq) {
	int result;
	try {
	    result = super.handleRequest(rq);
	} finally {
	    if (getResponseProperty("content-type") == null)
		setResponseProperty("content-type", "text/plain");
	}
	return result;
    }
}