0001: /*
0002: * Request.java
0003: *
0004: * Brazil project web application Framework,
0005: * export version: 1.1
0006: * Copyright (c) 1998-2000 Sun Microsystems, Inc.
0007: *
0008: * Sun Public License Notice
0009: *
0010: * The contents of this file are subject to the Sun Public License Version
0011: * 1.0 (the "License"). You may not use this file except in compliance with
0012: * the License. A copy of the License is included as the file "license.terms",
0013: * and also available at http://www.sun.com/
0014: *
0015: * The Original Code is from:
0016: * Brazil project web application Framework release 1.1.
0017: * The Initial Developer of the Original Code is: suhler.
0018: * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
0019: * All Rights Reserved.
0020: *
0021: * Contributor(s): cstevens, rinaldo, suhler.
0022: *
0023: * Version: 1.47
0024: * Created by suhler on 98/09/14
0025: * Last modified by suhler on 00/12/05 13:12:36
0026: */
0027:
0028: package sunlabs.brazil.server;
0029:
0030: import sunlabs.brazil.util.http.HttpInputStream;
0031: import sunlabs.brazil.util.http.HttpUtil;
0032: import sunlabs.brazil.util.http.MimeHeaders;
0033:
0034: import java.io.InputStream;
0035: import java.io.BufferedInputStream;
0036: import java.io.BufferedOutputStream;
0037: import java.io.FilterOutputStream;
0038: import java.io.OutputStream;
0039: import java.io.IOException;
0040: import java.net.Socket;
0041: import java.util.Hashtable;
0042: import java.util.Properties;
0043: import java.util.StringTokenizer;
0044:
0045: /**
0046: * Represents an HTTP transaction. A new instance is created
0047: * by the server for each connection.
0048: * <p>
0049: * Provides a set of accessor functions to fetch the individual fields
0050: * of the HTTP request.
0051: * <p>
0052: * Utility methods that are generically useful for manipulating HTTP
0053: * requests are included here as well. An instance of this class is
0054: * passed to handlers. There will be exactly one request object per thead
0055: * at any time.
0056: * <p>
0057: * The fields
0058: * {@link #headers},
0059: * {@link #query}, and
0060: * {@link #url}, and the method
0061: * {@link #getQueryData()}
0062: * are most often used to examine the content of the request.
0063: * The field
0064: * {@link #props}
0065: * contains information about the server, or up-stream handlers.
0066: * <p>
0067: * The methods
0068: * {@link #sendResponse(String, String, int)} and
0069: * {@link Request#sendError(int, String)}
0070: * are commonly used to return content to the client. The methods
0071: * {@link #addHeader(String)} and
0072: * {@link #setStatus(int)} can be used to modify the response headers
0073: * and return code respectively before the response is sent.
0074: * <p>
0075: * Many of the other methods are used internally, but can be useful to
0076: * handlers that need finer control over the output that the above methods
0077: * provide. Note that the order of the methods is important. For instance,
0078: * the user cannot change the HTTP response headers (by calling the
0079: * <code>addHeader</code> method or by modifying the
0080: * <code>responseHeaders</code> field) after having already sent an HTTP
0081: * response.
0082: * <p>
0083: * A number of the fields in the <code>Request</code> object are public,
0084: * by design. Many of the methods are convenience methods; the underlying
0085: * data fields are meant to be accessed for more complicated operations,
0086: * such as changing the URL or deleting HTTP response headers.
0087: *
0088: * @see Handler
0089: * @see Server
0090: *
0091: * @author Stephen Uhler (stephen.uhler@sun.com)
0092: * @author Colin Stevens (colin.stevens@sun.com)
0093: * @version 1.47, 00/12/05
0094: */
0095: public class Request {
0096: Server server;
0097: Socket sock;
0098: HttpInputStream in;
0099:
0100: /**
0101: * A set of properties local to this request. Created with
0102: * <code>server.props</code> as the default. This is useful for
0103: * handlers that wish to communicate via properties to down-stream
0104: * handlers, such as modifying a server property for a particular
0105: * request.
0106: * <p>
0107: * This variable is declared as a <code>Request.RechainableProperties</code>,
0108: * which provides the ability to reorder the properties in an existing
0109: * request, to insert a whole new set of properties into a request w/o
0110: * having to copy those properties one by one into the request's
0111: * properties. If the user does not need this functionality, this
0112: * variable may be accessed simply as a normal <code>Properties</code>.
0113: */
0114: public RechainableProperties props;
0115:
0116: /**
0117: * The HTTP response to the client is written to this stream. Normally
0118: * the convenience methods, such as <code>sendResponse</code>, are used
0119: * to send the response, but this field is available if a handler
0120: * needs to generate the response specially.
0121: * <p>
0122: * If the user chooses to write the response directly to this stream, the
0123: * user is still encouraged to use the convenience methods, such as
0124: * <code>sendHeaders</code>, to first send the HTTP response headers.
0125: * The {@link sunlabs.brazil.filter.FilterHandler}
0126: * examines the HTTP response headers
0127: * set by the convenience methods to determine whether to filter the
0128: * output.
0129: * <p>
0130: * Note that the HTTP response headers will <b>not</b> automatically be
0131: * sent as a side effect if the user writes to this stream. The user
0132: * would either need to call the convenience method
0133: * <code>sendHeaders</code> or need to generate the HTTP response headers
0134: * themselves.
0135: * <p>
0136: * This variable is declared as a <code>Request.HttpOutputStream</code>,
0137: * which provides the convenience method <code>writeBytes</code> to write
0138: * the byte representation of a string back to the client. If the user
0139: * does not need this functionality, this variable may be accessed
0140: * simply as a normal <code>OutputStream</code>.
0141: *
0142: * @see #sendResponse(String, String, int)
0143: * @see #sendHeaders(int, String, int)
0144: */
0145: public HttpOutputStream out;
0146:
0147: /*
0148: * How many requests this <code>Request</code> will handle. If this goes
0149: * to 0, then the connection will be closed even if
0150: * <code>keepAlive</code> is true.
0151: */
0152: private int requestsLeft;
0153:
0154: //-----------------------------------------------------------------------
0155:
0156: /**
0157: * The HTTP request method, such as "GET", "POST", or "PUT".
0158: */
0159: public String method;
0160:
0161: /**
0162: * The URL specified in the request, not including any "?" query
0163: * string.
0164: */
0165: public String url;
0166:
0167: /**
0168: * The query string specified after the URL, or <code>""</code> if no
0169: * query string was specified.
0170: */
0171: public String query;
0172:
0173: /**
0174: * The HTTP protocol specified in the request, either "HTTP/1.0" or
0175: * "HTTP/1.1".
0176: *
0177: * @see #version
0178: */
0179: public String protocol;
0180:
0181: /**
0182: * Derived from {@link #protocol}, the version of the HTTP protocol
0183: * used for this request. Either <code>10</code> for "HTTP/1.0" or
0184: * <code>11</code> for "HTTP/1.1".
0185: */
0186: public int version;
0187:
0188: /**
0189: * The HTTP request headers. Keys and values in this table correspond
0190: * the field names and values from each line in the HTTP header;
0191: * field names are case-insensitive, but the case of the values is
0192: * preserved. The order of entries in this table corresponds to the
0193: * order in which the request headers were seen. Multiple header lines
0194: * with the same key are stored as separate entries in the table.
0195: */
0196: public MimeHeaders headers;
0197:
0198: /**
0199: * The uploaded content of this request, usually from a POST. Set to
0200: * <code>null</code> if the request has no content.
0201: */
0202: public byte[] postData;
0203:
0204: /**
0205: * <code>true</code> if the client requested a persistent connection,
0206: * <code>false</code> otherwise. Derived from the {@link #protocol} and
0207: * the {@link #headers},
0208: * <p>
0209: * When "Keep-Alive" is requested, the client can issue multiple,
0210: * consecutive requests via a single socket connection. By default: <ul>
0211: * <li> HTTP/1.0 requests are not Keep-Alive, unless the
0212: * "Connection: Keep-Alive" header was present.
0213: * <li> HTTP/1.1 requests are Keep-Alive, unless the "Connection: close"
0214: * header was present.
0215: * </ul>
0216: * The user can change this value from <code>true</code> to
0217: * <code>false</code> to forcefully close the connection to the client
0218: * after sending the response. The user can change this value from
0219: * <code>false</code> to <code>true</code> if the client is using a
0220: * different header to request a persistent connection. See
0221: * {@link #connectionHeader}.
0222: * <p>
0223: * Regardless of this value, if an error is detected while receiving
0224: * or responding to an HTTP request, the connection will be closed.
0225: */
0226: public boolean keepAlive;
0227:
0228: /**
0229: * The header "Connection" usually controls whether the client
0230: * connection will be of type "Keep-Alive" or "close". The same
0231: * header is written back to the client in the response headers.
0232: * <p>
0233: * The field {@link #keepAlive} is set based on the value of the
0234: * "Connection" header. However, not all clients use "Connection"
0235: * to request that the connection be kept alive. For instance (although
0236: * it does not appear in the HTTP/1.0 or HTTP/1.1 documentation) both
0237: * Netscape and IE use the "Proxy-Connection" header when issuing
0238: * requests via an HTTP proxy. If a <code>Handler</code> is written to
0239: * respond to HTTP proxy requests, it should set <code>keepAlive</code>
0240: * depending on the value of the "Proxy-Connection" header, and set
0241: * <code>connectionHeader</code> to "Proxy-Connection", since the
0242: * convenience methods like <code>setResponse()</code> use these fields
0243: * when constructing the response. The server does not handle the
0244: * "Proxy-Connection" header by default, since trying to pre-anticipate
0245: * all the exceptions to the specification is a "slippery slope".
0246: */
0247: public String connectionHeader;
0248:
0249: //-----------------------------------------------------------------------
0250:
0251: int statusCode;
0252: String statusPhrase;
0253:
0254: /**
0255: * The HTTP response headers. Keys and values in this table correspond
0256: * to the HTTP headers that will be written back to the client when
0257: * the response is sent. The order of entries in this table corresponds
0258: * to the order in which the HTTP headers will be sent. Multiple header
0259: * lines with the same key will be stored as separate entries in the
0260: * table.
0261: *
0262: * @see #addHeader(String, String)
0263: */
0264: public MimeHeaders responseHeaders;
0265:
0266: /*
0267: * True if the headers have already been sent, so that if sendError()
0268: * is called in the middle of sending a response, it won't send the
0269: * headers again, but will cause the connection to be closed afterwards.
0270: */
0271: private boolean headersSent;
0272:
0273: /**
0274: * Time stamp for start of this request - set, but not used.
0275: */
0276: public long startMillis;
0277:
0278: /**
0279: * Create a new http request. Requests are created by the server for
0280: * use by handlers.
0281: *
0282: * @param server
0283: * The server that owns this request.
0284: *
0285: * @param sock
0286: * The socket of the incoming HTTP request.
0287: */
0288: Request(Server server, Socket sock) {
0289: this .server = server;
0290: this .sock = sock;
0291:
0292: try {
0293: in = new HttpInputStream(new BufferedInputStream(sock
0294: .getInputStream()));
0295: out = new HttpOutputStream(new BufferedOutputStream(sock
0296: .getOutputStream()));
0297: } catch (IOException e) {
0298: /*
0299: * Logically we shouldn't get an error obtaining the streams from
0300: * the socket, but if it does, it will be caught later as a
0301: * NullPointerException by the Connection.run() method the first
0302: * time we attempt to read from the socket.
0303: */
0304: }
0305:
0306: requestsLeft = server.maxRequests;
0307: keepAlive = true;
0308:
0309: props = new RechainableProperties(server.props);
0310:
0311: headers = new MimeHeaders();
0312: responseHeaders = new MimeHeaders();
0313: }
0314:
0315: /**
0316: * Returns a string representation of this <code>Request</code>.
0317: * The string representation is the first line (the method line) of the
0318: * HTTP request that this <code>Request</code> is handling. Useful for
0319: * debugging.
0320: *
0321: * @return The string representation of this <code>Request</code>.
0322: */
0323: public String toString() {
0324: StringBuffer sb = new StringBuffer();
0325:
0326: sb.append(method).append(' ').append(url);
0327: if ((query != null) && (query.length() > 0)) {
0328: sb.append('?').append(query);
0329: }
0330: sb.append(' ').append(protocol);
0331: return sb.toString();
0332: }
0333:
0334: /**
0335: * Reads an HTTP request from the socket.
0336: *
0337: * @param props The server props associated with this request
0338: *
0339: * @return <code>true</code> if the request was successfully read and
0340: * parsed, <code>false</code> if the request was malformed.
0341: *
0342: * @throws IOException
0343: * if there was an IOException reading from the socket. See
0344: * the socket documentation for a description of socket
0345: * exceptions.
0346: */
0347: boolean getRequest(Properties defaults) throws IOException {
0348: /*
0349: * Reset state.
0350: */
0351:
0352: requestsLeft--;
0353: connectionHeader = "Connection";
0354: props.clear();
0355: props.setDefaults(defaults);
0356:
0357: method = null;
0358: url = null;
0359: query = null;
0360: protocol = null;
0361: headers.clear();
0362: postData = null;
0363:
0364: statusCode = 200;
0365: statusPhrase = "OK";
0366: responseHeaders.clear();
0367: startMillis = System.currentTimeMillis();
0368: out.bytesWritten = 0;
0369:
0370: /*
0371: * Get first line of HTTP request (the method line).
0372: */
0373:
0374: String line;
0375: while (true) {
0376: line = in.readLine();
0377: if (line == null) {
0378: return false;
0379: } else if (line.length() > 0) {
0380: break;
0381: }
0382: log(Server.LOG_INFORMATIONAL, "Skipping blank line");
0383: }
0384:
0385: log(Server.LOG_LOG, "Request " + requestsLeft + " " + line);
0386:
0387: StringTokenizer st = new StringTokenizer(line);
0388: int count = st.countTokens();
0389: if (count != 3) {
0390: sendError(400, line, null);
0391: return false;
0392: }
0393:
0394: method = st.nextToken();
0395: url = st.nextToken();
0396: protocol = st.nextToken();
0397:
0398: if ((method.equals("GET") == false)
0399: && (method.equals("POST") == false)
0400: && (method.equals("PUT") == false)) {
0401: sendError(501, method, null);
0402: return false;
0403: }
0404:
0405: if (protocol.equals("HTTP/1.0")) {
0406: version = 10;
0407: } else if (protocol.equals("HTTP/1.1")) {
0408: version = 11;
0409: } else {
0410: sendError(505, line, null);
0411: return false;
0412: }
0413:
0414: /*
0415: * Separate query string from URL.
0416: */
0417:
0418: int index = url.indexOf('?');
0419: if (index >= 0) {
0420: query = url.substring(index + 1);
0421: url = url.substring(0, index);
0422: } else {
0423: query = "";
0424: }
0425:
0426: headers.read(in);
0427:
0428: /*
0429: * Remember POST data. "Transfer-Encoding: chunked" is not handled
0430: * yet.
0431: */
0432:
0433: String str;
0434:
0435: str = getRequestHeader("Content-Length");
0436: if (str != null) {
0437: try {
0438: postData = new byte[Integer.parseInt(str)];
0439: } catch (Exception e) {
0440: sendError(411, str, null);
0441: return false;
0442: } catch (OutOfMemoryError e) {
0443: sendError(411, str, null);
0444: return false;
0445: }
0446: in.readFully(postData);
0447: }
0448:
0449: str = getRequestHeader(connectionHeader);
0450: if ("Keep-Alive".equalsIgnoreCase(str)) {
0451: keepAlive = true;
0452: } else if ("close".equalsIgnoreCase(str)) {
0453: keepAlive = false;
0454: } else if (version > 10) {
0455: keepAlive = true;
0456: } else {
0457: keepAlive = false;
0458: }
0459:
0460: return true;
0461: }
0462:
0463: boolean shouldKeepAlive() {
0464: return (requestsLeft > 0) && keepAlive;
0465: }
0466:
0467: /**
0468: * The socket from which the HTTP request was received, and to where the
0469: * HTTP response will be written. The user should not directly read from
0470: * or write to this socket. The socket is provided other purposes, for
0471: * example, imagine a handler that provided different content depending
0472: * upon the IP address of the client.
0473: *
0474: * @return The client socket that issued this HTTP request.
0475: */
0476: public Socket getSocket() {
0477: return sock;
0478: }
0479:
0480: /**
0481: * Logs a message by calling <code>Server.log</code>. Typically a
0482: * message is generated on the console or in a log file, if the
0483: * <code>level</code> is less than the current server log setting.
0484: *
0485: * @param level
0486: * The severity of the message.
0487: *
0488: * @param message
0489: * The message that will be logged.
0490: *
0491: * @see Server#log(int, Object, String)
0492: */
0493: public void log(int level, String message) {
0494: server.log(level, null, message);
0495: }
0496:
0497: /**
0498: * Logs a message by calling <code>Server.log</code>. Typically a
0499: * message is generated on the console or in a log file, if the
0500: * <code>level</code> is less than the current server log setting.
0501: *
0502: * @param level
0503: * The severity of the message.
0504: *
0505: * @param obj
0506: * The object that the message relates to.
0507: *
0508: * @param message
0509: * The message that will be logged.
0510: *
0511: * @see Server#log(int, Object, String)
0512: */
0513: public void log(int level, Object obj, String message) {
0514: server.log(level, obj, message);
0515: }
0516:
0517: /*
0518: *-----------------------------------------------------------------------
0519: * Request methods.
0520: *-----------------------------------------------------------------------
0521: */
0522:
0523: /**
0524: * Returns the value that the given case-insensitive key maps to
0525: * in the HTTP request headers. In order to do fancier things like
0526: * changing or deleting an existing request header, the user may directly
0527: * access the <code>headers</code> field.
0528: *
0529: * @param key
0530: * The key to look for in the HTTP request headers. May not
0531: * be <code>null</code>.
0532: *
0533: * @return The value to which the given key is mapped, or
0534: * <code>null</code> if the key is not in the headers.
0535: *
0536: * @see #headers
0537: */
0538: public String getRequestHeader(String key) {
0539: return headers.get(key);
0540: }
0541:
0542: /*
0543: *-----------------------------------------------------------------------
0544: * Response methods.
0545: *-----------------------------------------------------------------------
0546: */
0547:
0548: /**
0549: * Sets the status code of the HTTP response. The default status
0550: * code for a response is <code>200</code> if this method is not
0551: * called.
0552: * <p>
0553: * An HTTP status phrase will be chosen based on the given
0554: * status code. For example, the status code <code>404</code> will get
0555: * the status phrase "Not Found".
0556: * <p>
0557: * If this method is called, it must be called before
0558: * <code>sendHeaders</code> is either directly or indirectly called.
0559: * Otherwise, it will have no effect.
0560: *
0561: * @param code
0562: * The HTTP status code, such as <code>200</code> or
0563: * <code>404</code>. If < 0, the HTTP status code will
0564: * not be changed.
0565: *
0566: * @see #sendHeaders(int, String, int)
0567: */
0568: public void setStatus(int code) {
0569: if (code >= 0) {
0570: setStatus(code, HttpUtil.getStatusPhrase(code));
0571: }
0572:
0573: }
0574:
0575: /**
0576: * Set the HTTP status code and status phrase of this request. The given
0577: * status will be sent to the client when the user directly or indirectly
0578: * calls the method <code>sendHeaders</code>. The given status phrase
0579: * replaces the default HTTP status phrase normally associated with the
0580: * given status code.
0581: *
0582: * @param code
0583: * The HTTP status code, such as <code>200</code> or
0584: * <code>404</code>.
0585: *
0586: * @param message
0587: * The HTTP status phrase, such as <code>"Okey dokey"</code> or
0588: * <code>"I don't see it"</code>.
0589: *
0590: * @see #sendHeaders(int, String, int)
0591: */
0592: private void setStatus(int code, String message) {
0593: this .statusCode = code;
0594: this .statusPhrase = message;
0595: }
0596:
0597: /**
0598: * Return the status code.
0599: */
0600:
0601: public int getStatus() {
0602: return statusCode;
0603: }
0604:
0605: /**
0606: * Return uses of this socket
0607: */
0608:
0609: public int getReuseCount() {
0610: return server.maxRequests - requestsLeft;
0611: }
0612:
0613: /**
0614: * Adds a response header to the HTTP response. In order to do fancier
0615: * things like appending a value to an existing response header, the
0616: * user may directly access the <code>responseHeaders</code> field.
0617: * <p>
0618: * If this method is called, it must be called before
0619: * <code>sendHeaders</code> is either directly or indirectly called.
0620: * Otherwise, it will have no effect.
0621: *
0622: * @param key
0623: * The header name.
0624: *
0625: * @param value
0626: * The value for the request header.
0627: *
0628: * @see #sendHeaders(int, String, int)
0629: * @see #responseHeaders
0630: */
0631: public void addHeader(String key, String value) {
0632: responseHeaders.add(key, value);
0633: }
0634:
0635: /**
0636: * Adds a response header to the HTTP response. In order to do fancier
0637: * things like appending a value to an existing response header, the
0638: * user may directly access the <code>responseHeaders</code> field.
0639: * <p>
0640: * If this method is called, it must be called before
0641: * <code>sendHeaders</code> is either directly or indirectly called.
0642: * Otherwise, it will have no effect.
0643: *
0644: * @param line
0645: * The HTTP response header, of the form
0646: * "<code>key</code>: <code>value</code>".
0647: *
0648: * @see #sendHeaders(int, String, int)
0649: * @see #responseHeaders
0650: */
0651: public void addHeader(String line) {
0652: int dots = line.indexOf(':');
0653: String key = line.substring(0, dots);
0654: String value = line.substring(dots + 1).trim();
0655: addHeader(key, value);
0656: }
0657:
0658: /**
0659: * Sends an HTTP response to the client.
0660: * <p>
0661: * This method first calls <code>sendHeaders</code> to send the HTTP
0662: * response headers, then sends the given byte array as the HTTP
0663: * response body.
0664: * <p>
0665: * The "Content-Length" will be set to the length of the given byte array.
0666: * The "Content-Type" will be set to the given MIME type.
0667: *
0668: * @param body
0669: * The array of bytes to send as the HTTP response body. May
0670: * not be <code>null</code>.
0671: *
0672: * @param type
0673: * The MIME type of the response, such as "text/html". May be
0674: * <code>null</code> to use the existing "Content-Type"
0675: * response header (if any).
0676: *
0677: * @throws IOException
0678: * if there was an I/O error while sending the response to
0679: * the client.
0680: *
0681: * @see #sendHeaders(int, String, int)
0682: */
0683: public void sendResponse(byte[] body, String type)
0684: throws IOException {
0685: sendHeaders(-1, type, body.length);
0686: out.write(body);
0687: }
0688:
0689: /**
0690: * Sends an HTTP response to the client.
0691: * <p>
0692: * This method first calls <code>sendHeaders</code> to send the HTTP
0693: * response headers. It then writes out the given string to the client
0694: * as a sequence of bytes. Each character in the string is written out
0695: * by discarding its high eight bits.
0696: * <p>
0697: * The "Content-Length" will be set to the length of the string.
0698: * The "Content-Type" will be set to the given MIME type.
0699: *
0700: * @param body
0701: * The string to send as the HTTP response body. May
0702: * not be <code>null</code>.
0703: *
0704: * @param type
0705: * The MIME type of the response, such as "text/html". May be
0706: * <code>null</code> to preserve the existing "Content-Type"
0707: * response header (if any).
0708: *
0709: * @param code
0710: * The HTTP status code for the response, such as
0711: * <code>200</code>. May be < 0 to preserve the existing
0712: * status code.
0713: *
0714: * @throws IOException
0715: * if there was an I/O error while sending the response to
0716: * the client.
0717: *
0718: * @see #sendHeaders(int, String, int)
0719: */
0720: public void sendResponse(String body, String type, int code)
0721: throws IOException {
0722: sendHeaders(code, type, body.length());
0723: out.writeBytes(body);
0724: }
0725:
0726: /**
0727: * Convenience method that sends an HTTP response to the client
0728: * with a "Content-Type" of "text/html" and the default HTTP status
0729: * code.
0730: *
0731: * @param body
0732: * The string to send as the HTTP response body.
0733: *
0734: * @see #sendResponse(String, String, int)
0735: */
0736: public void sendResponse(String body) throws IOException {
0737: sendResponse(body, "text/html", -1);
0738: }
0739:
0740: /**
0741: * Convenience method that sends an HTTP response to the client
0742: * with the default HTTP status code.
0743: *
0744: * @param body
0745: * The string to send as the HTTP response body.
0746: *
0747: * @param type
0748: * The MIME type of the response.
0749: *
0750: * @see #sendResponse(String, String, int)
0751: */
0752: public void sendResponse(String body, String type)
0753: throws IOException {
0754: sendResponse(body, type, -1);
0755: }
0756:
0757: /**
0758: * Sends the contents of the given input stream as the HTTP response.
0759: * <p>
0760: * This method first calls <code>sendHeaders</code> to send the HTTP
0761: * response headers. It then transfers a total of <code>length</code>
0762: * bytes of data from the given input stream to the client as the
0763: * HTTP response body.
0764: * <p>
0765: * This method takes care of setting the "Content-Length" header
0766: * if the actual content length is known, or the "Transfer-Encoding"
0767: * header if the content length is not known (for HTTP/1.1 clients only).
0768: * <p>
0769: * This method may set the <code>keepAlive</code> to <code>false</code>
0770: * before returning, if fewer than <code>length</code> bytes could be
0771: * read.
0772: *
0773: * @param in
0774: * The input stream to read from.
0775: *
0776: * @param length
0777: * The content length. The number of bytes to send to the
0778: * client. May be < 0, in which case this method will read
0779: * until reaching the end of the input stream.
0780: *
0781: * @param type
0782: * The MIME type of the response, such as "text/html". May be
0783: * <code>null</code> to preserve the existing "Content-Type"
0784: * response header (if any).
0785: *
0786: * @param code
0787: * The HTTP status code for the response, such as
0788: * <code>200</code>. May be < 0 to preserve the existing
0789: * status code.
0790: *
0791: * @throws IOException
0792: * if there was an I/O error while sending the response to
0793: * the client.
0794: */
0795: public void sendResponse(InputStream in, int length, String type,
0796: int code) throws IOException {
0797: HttpInputStream hin = new HttpInputStream(in);
0798:
0799: byte[] buf = new byte[server.bufsize];
0800:
0801: if (length >= 0) {
0802: sendHeaders(code, type, length);
0803: if (hin.copyTo(out, length, buf) != length) {
0804: keepAlive = false;
0805: }
0806: } else if (version <= 10) {
0807: keepAlive = false;
0808: sendHeaders(code, type, -1);
0809: hin.copyTo(out, -1, buf);
0810: } else {
0811: addHeader("Transfer-Encoding", "chunked");
0812: sendHeaders(code, type, -1);
0813:
0814: while (true) {
0815: int count = hin.read(buf);
0816: if (count < 0) {
0817: out.writeBytes("0\r\n\r\n");
0818: break;
0819: }
0820: out.writeBytes(Integer.toHexString(count) + "\r\n");
0821: out.write(buf, 0, count);
0822: out.writeBytes("\r\n");
0823: }
0824: }
0825: }
0826:
0827: /**
0828: * Sends a HTTP error response to the client.
0829: *
0830: * @param code
0831: * The HTTP status code.
0832: *
0833: * @param clientMessage
0834: * A short message to be included in the error response
0835: * and logged to the server.
0836: */
0837: public void sendError(int code, String clientMessage) {
0838: sendError(code, clientMessage, null);
0839: }
0840:
0841: /**
0842: * Sends a HTTP error response to the client.
0843: *
0844: * @param code
0845: * The HTTP status code.
0846: *
0847: * @param clientMessage
0848: * A short message to be included in the error response.
0849: *
0850: * @param logMessage
0851: * A short message to be logged to the server. This message is
0852: * <b>not</b> sent to the client.
0853: */
0854: public void sendError(int code, String clientMessage,
0855: String logMessage) {
0856: setStatus(code);
0857: server.errorCount++;
0858:
0859: String message = clientMessage;
0860: if (message == null) {
0861: message = logMessage;
0862: logMessage = null;
0863: }
0864: log(Server.LOG_LOG, "Error", statusCode + " " + statusPhrase
0865: + ": " + message);
0866: if (logMessage != null) {
0867: log(Server.LOG_LOG, logMessage);
0868: }
0869:
0870: keepAlive = false;
0871: if (headersSent) {
0872: /*
0873: * The headers have already been sent. We can't send an error
0874: * message in the middle of an existing response, so just close
0875: * this request.
0876: */
0877:
0878: return;
0879: }
0880:
0881: String body = "<html>\n<head>\n"
0882: + "<title>Error: "
0883: + statusCode
0884: + "</title>\n"
0885: + "<body>\nGot the error: <b>"
0886: + statusPhrase
0887: + "</b><br>\nwhile trying to obtain <b>"
0888: + ((url == null) ? "unknown URL" : HttpUtil
0889: .htmlEncode(url)) + "</b><br>\n"
0890: + HttpUtil.htmlEncode(clientMessage)
0891: + "\n</body>\n</html>";
0892:
0893: try {
0894: sendResponse(body, "text/html", statusCode);
0895: } catch (IOException e) {
0896: /*
0897: * Don't throw an error in the process of sending an error
0898: * message!
0899: */
0900: }
0901: }
0902:
0903: /**
0904: * Sends the HTTP status line and response headers to the client. This
0905: * method is automatically invoked by <code>sendResponse</code>, but
0906: * can be manually invoked if the user needs direct access to the
0907: * client's output stream. If this method is not called, then the
0908: * HTTP status and response headers will not automatically be sent to
0909: * the client; the user would be responsible for forming the entire
0910: * HTTP response.
0911: * <p>
0912: * The user may call the <code>addHeader</code> method or modify the
0913: * <code>responseHeaders</code> field before calling this method.
0914: * This method then adds a number of HTTP headers, as follows: <ul>
0915: * <li> "Date" - the current time, if this header is not already present.
0916: * <li> "Server" - the server's name (from <code>server.name</code>), if
0917: * this header is not already present.
0918: * <li> "Connection" - "Keep-Alive" or "close", depending upon the
0919: * <code>keepAlive</code> field.
0920: * <li> "Content-Length" - set to the given <code>length</code>.
0921: * <li> "Content-Type" - set to the given <code>type</code>.
0922: * </ul>
0923: * <p>
0924: * The string used for "Connection" header actually comes from the
0925: * <code>connectionHeader</code> field.
0926: *
0927: * @param code
0928: * The HTTP status code for the response, such as
0929: * <code>200</code>. May be < 0 to preserve the existing
0930: * status code.
0931: *
0932: * @param type
0933: * The MIME type of the response, such as "text/html". May be
0934: * <code>null</code> to preserve the existing "Content-Type"
0935: * response header (if any).
0936: *
0937: * @param length
0938: * The length of the response body. May be < 0 if the length
0939: * is unknown and/or to preserve the existing "Content-Length"
0940: * response header (if any).
0941: *
0942: * @throws IOException
0943: * if there was an I/O error while sending the headers to
0944: * the client.
0945: *
0946: * @see #setStatus(int)
0947: * @see #addHeader(String, String)
0948: * @see #sendResponse(String, String, int)
0949: * @see #connectionHeader
0950: */
0951: public void sendHeaders(int code, String type, int length)
0952: throws IOException {
0953: setStatus(code);
0954: if ((length == 0) && (statusCode == 200)) {
0955: /*
0956: * No Content.
0957: */
0958: setStatus(204);
0959: }
0960:
0961: responseHeaders.putIfNotPresent("Date", HttpUtil.formatTime());
0962: if (server.name != null) {
0963: responseHeaders.putIfNotPresent("Server", server.name);
0964: }
0965: String str = shouldKeepAlive() ? "Keep-Alive" : "close";
0966: responseHeaders.put(connectionHeader, str);
0967: if (length >= 0) {
0968: responseHeaders.put("Content-Length", Integer
0969: .toString(length));
0970: }
0971: if (type != null) {
0972: responseHeaders.put("Content-Type", type);
0973: }
0974:
0975: out.sendHeaders(this );
0976: headersSent = true;
0977: }
0978:
0979: /**
0980: * Send the response headers to the client.
0981: * This consists of standard plus added headers. The handler is reponsible
0982: * for sending the reponse body.
0983: *
0984: * @param type The document mime type
0985: * @param length the document length
0986: *
0987: * @see Request#addHeader(String)
0988: * @see Request#sendResponse(String)
0989: * @see Request#setStatus(int)
0990: */
0991:
0992: /**
0993: * Responds to an HTTP request with a redirection reply, telling the
0994: * client that the requested url has moved. Generally, this is used if
0995: * the client did not put a '/' on the end of a directory.
0996: *
0997: * @param url
0998: * The URL the client should have requested. This URL may be
0999: * fully-qualified (in the form "http://....") or host-relative
1000: * (in the form "/...").
1001: *
1002: * @param body
1003: * The body of the redirect response, or <code>null</code> to
1004: * send a hardcoded message.
1005: */
1006: public void redirect(String url, String body) throws IOException {
1007: if (url.startsWith("/")) {
1008: url = serverUrl() + url;
1009: }
1010: addHeader("Location", url);
1011: if (body == null) {
1012: body = "<title>Moved</title><h1>look for <a href=" + url
1013: + ">" + url + "</h1>";
1014: }
1015: sendResponse(body, "text/html", 302);
1016: }
1017:
1018: /**
1019: * Returns the server's fully-qualified base URL. This is "http://"
1020: * followed by the server's hostname and port.
1021: * <p>
1022: * If the HTTP request header "Host" is present, it specifies the
1023: * hostname and port that will be used instead of the server's internal
1024: * name for itself. Due bugs in certain browsers, when using the server's
1025: * internal name, the port number will be elided if it is <code>80</code>.
1026: *
1027: * @return The string representation of the server's URL.
1028: */
1029: public String serverUrl() {
1030: String host = headers.get("Host");
1031: if (host == null) {
1032: host = server.hostName;
1033: }
1034: int index = host.lastIndexOf(":");
1035: if ((index < 0) && (server.listen.getLocalPort() != 80)) {
1036: host += ":" + server.listen.getLocalPort();
1037: }
1038: return server.protocol + "://" + host;
1039: }
1040:
1041: /**
1042: * Retrieves the query data as a hashtable.
1043: * This includes both the query information included as part of the url
1044: * and any posted "application/x-www-form-urlencoded" data.
1045: *
1046: * @param table
1047: * An existing hashtable in which to put the query data as
1048: * name/value pairs. May be <code>null</code>, in which case
1049: * a new hashtable is allocated.
1050: *
1051: * @returns The hashtable in which the query data was stored.
1052: */
1053: public Hashtable getQueryData(Hashtable table) {
1054: if (table == null) {
1055: table = new Hashtable();
1056: }
1057: HttpUtil.extractQuery(query, table);
1058: if (postData != null) {
1059: String contentType = headers.get("Content-Type");
1060: if ("application/x-www-form-urlencoded".equals(contentType)) {
1061: HttpUtil.extractQuery(new String(postData), table);
1062: }
1063: }
1064: return table;
1065: }
1066:
1067: /**
1068: * Retrieves the query data as a hashtable.
1069: * This includes both the query information included as part of the url
1070: * and any posted "application/x-www-form-urlencoded" data.
1071: *
1072: * @returns The hashtable in which the query data was stored.
1073: */
1074: public Hashtable getQueryData() {
1075: return getQueryData(null);
1076: }
1077:
1078: /**
1079: * The <code>RechainableProperties</code> is similar to a standard
1080: * <code>Properties</code>, except that the defaults can be changed
1081: * at any time. The defaults for a standard <code>Properties</code>
1082: * object can only be set once, when the object is constructed.
1083: * <p>
1084: * It is is useful in a certain set of circumstances to build a chain
1085: * of request properties, and be able to insert new sets of properties
1086: * anywhere in the chain.
1087: * <p>
1088: * NOTE:<br>This implementation is fundamentally broken. By using the
1089: * built-in Properties class, the data is bound to the link pointer, so
1090: * one table may be chained onto excactly one list.
1091: */
1092: public static class RechainableProperties extends Properties {
1093: public RechainableProperties() {
1094: this (null);
1095: }
1096:
1097: public RechainableProperties(Properties defaults) {
1098: super (defaults);
1099: }
1100:
1101: /**
1102: * Set the default properties for this object.
1103: * @param change The new default properties. Use this
1104: * with care: no attempt is made to detect loops.
1105: * @returns The previous value of the default properties.
1106: */
1107:
1108: public Properties setDefaults(Properties change) {
1109: /*
1110: if (change == this) {
1111: throw new RuntimeException("Infinite loop in properties chain");
1112: }
1113: */
1114: Properties old = this .defaults;
1115: this .defaults = change;
1116: return old;
1117: }
1118:
1119: /**
1120: * Return the current "default" properties for this object.
1121: */
1122:
1123: public Properties getDefaults() {
1124: return this .defaults;
1125: }
1126: }
1127:
1128: /**
1129: * The <code>HttpOutputStream</code> provides the convenience method
1130: * <code>writeBytes</code> for writing the byte representation of a
1131: * string, without bringing in the overhead and the deprecated warnings
1132: * associated with a <code>java.io.DataOutputStream</code>.
1133: * <p>
1134: * The other methods in this class are here to allow the
1135: * <code>FilterHandler</code> and <code>ChainSawHandler</code> to
1136: * alter the behavior in an implememtation specific way. This behavior
1137: * is unfortunate, and might go away when a better strategy comes along.
1138: */
1139: public static class HttpOutputStream extends FilterOutputStream {
1140: /**
1141: * Count the number of bytes that are written to this stream
1142: */
1143:
1144: public int bytesWritten = 0;
1145:
1146: public HttpOutputStream(OutputStream out) {
1147: super (out);
1148: }
1149:
1150: public void writeBytes(String s) throws IOException {
1151: int len = s.length();
1152: for (int i = 0; i < len; i++) {
1153: this .out.write((byte) s.charAt(i));
1154: }
1155: }
1156:
1157: public void write(byte b) throws IOException {
1158: this .out.write(b);
1159: bytesWritten++;
1160: }
1161:
1162: public void write(byte[] buf, int off, int len)
1163: throws IOException {
1164: this .out.write(buf, off, len);
1165: bytesWritten += len;
1166: }
1167:
1168: public void sendHeaders(Request request) throws IOException {
1169: writeBytes(request.protocol + " " + request.statusCode
1170: + " " + request.statusPhrase + "\r\n");
1171: request.responseHeaders.print(this .out);
1172: writeBytes("\r\n");
1173: }
1174: }
1175: }
|