001: /*
002: HttpdBase4J: An embeddable Java web server framework that supports HTTP, HTTPS,
003: templated content and serving content from inside a jar or archive.
004: Copyright (C) 2007 Donald Munro
005:
006: This library is free software; you can redistribute it and/or
007: modify it under the terms of the GNU Lesser General Public
008: License as published by the Free Software Foundation; either
009: version 2.1 of the License, or (at your option) any later version.
010:
011: This library is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public
017: License along with this library; if not,see http://www.gnu.org/licenses/lgpl.txt
018: */
019:
020: package net.homeip.donaldm.httpdbase4j;
021:
022: import com.sun.net.httpserver.HttpExchange;
023: import com.sun.net.httpserver.HttpHandler;
024: import java.io.BufferedInputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.net.URI;
028: import java.util.Iterator;
029: import java.util.List;
030: import net.homeip.donaldm.httpdbase4j.Httpd.LogLevel;
031:
032: /**
033: * An abstraction of a handler for HTTP requests.
034: * @author Donald Munro
035: */
036: abstract public class RequestHandler implements HttpHandler
037: //==========================================================
038: {
039:
040: /**
041: The Httpd instance for this request.
042: */
043: protected Httpd m_httpd = null;
044:
045: /**
046: The HttpExchange instance for this request.
047: * @see com.sun.net.httpserver.HttpExchange
048: */
049: protected HttpExchange m_ex = null;
050:
051: /**
052: * Logging verbosity on or off.
053: */
054: protected boolean m_verbose = true;
055:
056: /**
057: * Delimiter to use between files in a combined request. Must be a regular
058: * expression, hence the \\+.
059: * @see CombinedRequest
060: */
061: static protected String COMBINED_REQUEST_DELIMITER = "!\\+!";
062:
063: static protected String[] COMBINED_REQUEST_EXTENSIONS = { ".css",
064: ".js" };
065:
066: /**
067: * Create a RequestHandler
068: * @param httpd The HttpExchange instance for this request.
069: * @see com.sun.net.httpserver.HttpExchange
070: * @param isVerbose Logging verbosity on/off
071: */
072: public RequestHandler(Httpd httpd, boolean isVerbose)
073: //--------------------------------------
074: {
075: m_httpd = httpd;
076: m_verbose = isVerbose;
077: }
078:
079: static public boolean isCombinedRequest(String path)
080: //--------------------------------------------------
081: {
082: if (!path.matches(".*" + COMBINED_REQUEST_DELIMITER + ".*"))
083: return false;
084: for (int i = 0; i < COMBINED_REQUEST_EXTENSIONS.length; i++)
085: if (path.contains(COMBINED_REQUEST_EXTENSIONS[i]))
086: return true;
087: return false;
088: }
089:
090: @Override
091: public String toString()
092: //----------------------
093: {
094:
095: return super .toString() + Http.strExchange(m_ex);
096: }
097:
098: /**
099: * Permissions check for directory browse
100: * @param ex The HttpExchange instance for this request.
101: * @param request The request to check
102: */
103: protected void browseDirCheck(HttpExchange ex, Request request)
104: //-------------------------------------------------------------
105: {
106: if (!request.isDirectory()) {
107: try {
108: HttpResponse.internalError(ex, request.getURI(),
109: ex.getRequestHeaders()).send();
110: } catch (Exception e) {
111: Httpd.Log(LogLevel.ERROR, "Creating URI /", e);
112: }
113: return;
114: }
115:
116: URI uri = null;
117: try {
118: uri = new URI(request.getURI().toString() + "/");
119: } catch (Exception e) {
120: uri = null;
121: }
122: if (m_httpd.onAllowDirectoryBrowse(request.getAbsolutePath())) {
123: if (!request.getPath().endsWith("/")) {
124: if (uri != null)
125: HttpResponse.reDirect(ex, uri,
126: ex.getRequestHeaders()).send();
127: else {
128: try {
129: HttpResponse.internalError(ex,
130: request.getURI(),
131: ex.getRequestHeaders()).send();
132: } catch (Exception e) {
133: Httpd.Log(LogLevel.ERROR, "Creating URI /", e);
134: }
135: }
136: return;
137: }
138: } else {
139: HttpResponse.accessDenied(ex, uri, ex.getRequestHeaders())
140: .send();
141: return;
142: }
143: }
144:
145: protected void sendResult(Request request, HttpResponse r, long id,
146: String etag, HttpExchange ex)
147: //-------------------------------------------------------------------------
148: {
149: HttpHandleable handler = request.getHandler();
150: if (!request.getContent(id, handler)) {
151: HttpResponse.internalError(ex, request.getURI(),
152: ex.getRequestHeaders()).send();
153: return;
154: }
155: long len = request.getContentLength();
156: boolean isModified = false;
157: HttpResponse userResponse = handler.onServeHeaders(id, ex,
158: request);
159: if (userResponse == null) {
160: String mimeType = Http.getMimeType(request);
161: if (mimeType == null)
162: mimeType = "text/plain";
163: List<String> l = ex.getRequestHeaders().get("Accept");
164: boolean isFound = false;
165: for (Iterator<String> it = l.iterator(); it.hasNext();) {
166: String accept = it.next();
167: if (accept == null)
168: continue;
169: accept = accept.trim();
170: if ((accept != null)
171: && (mimeType != null)
172: && ((accept.contains(mimeType)) || (accept
173: .compareTo("*/*") == 0))) {
174: r.addHeader("Content-Type", mimeType);
175: isFound = true;
176: break;
177: }
178: }
179: if (!isFound)
180: r.addHeader("Content-Type", mimeType);
181: } else {
182: isModified = true;
183: r = userResponse;
184: }
185: if (request.isCacheable()) {
186: if (etag != null)
187: r.addHeader("ETag", etag);
188: r.addHeader("Last-Modified", Http
189: .strDate(request.getDate()));
190: }
191: if (request.m_encoding != null)
192: r.addHeader("Content-Encoding", request.m_encoding);
193: r.addHeader("Connection", "close");
194: if (request.getMethod() == Request.HTTP_METHOD.HEAD)
195: r.sendHeaders(-1);
196: else {
197: InputStream is = handler.onServeBody(id, ex, request);
198: if (is != null) {
199: String s = r.getHeader("Content-Length", 0);
200: try {
201: len = Long.parseLong(s);
202: } catch (Exception e) {
203: len = -1;
204: }
205: if ((!isModified) || (len < 0)) {
206: Httpd.Log(LogLevel.ERROR,
207: "Content-Length not set in "
208: + "header in onGETHeaders.", null);
209: HttpResponse.internalError(ex, request.getURI(),
210: ex.getRequestHeaders()).send();
211: return;
212: }
213: }
214: if (r.sendHeaders(len)) {
215: BufferedInputStream bis = null;
216: boolean ok = false;
217: try {
218: if (is == null) {
219: bis = new BufferedInputStream(request
220: .getStream());
221: ok = r.sendData(bis);
222: } else
223: ok = r.sendData(is);
224:
225: } catch (Exception e) {
226: Httpd.Log(Httpd.LogLevel.ERROR,
227: "Error reading file", e);
228: ok = false;
229: } finally {
230: if (bis != null)
231: try {
232: bis.close();
233: } catch (Exception e) {
234: }
235: if (is != null)
236: try {
237: is.close();
238: } catch (Exception e) {
239: }
240: }
241: handler.onPostServe(id, ex, request, ok);
242: }
243: }
244: }
245:
246: /* (non-Javadoc)
247: * @see com.sun.net.httpserver.HttpHandler#handle(com.sun.net.httpserver.HttpExchange)
248: */
249: abstract public void handle(HttpExchange exchange)
250: throws IOException;
251: }
|