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.Headers;
023: import com.sun.net.httpserver.HttpExchange;
024: import java.io.ByteArrayInputStream;
025: import java.io.InputStream;
026: import java.io.OutputStream;
027: import java.net.URI;
028: import java.util.ArrayList;
029: import java.util.List;
030:
031: /**
032: * Encapsulates sending HTTP responses and provides several static methods
033: * for sending common HTTP error and informational responses.
034: * @author Donald Munro
035: */
036: public class HttpResponse
037: //=======================
038: {
039: protected int m_status = 200;
040:
041: protected Headers m_headers = null;
042:
043: protected HttpExchange m_exchange = null;
044:
045: private String m_body = "";
046:
047: private OutputStream m_responseStream = null;
048:
049: private long m_contentLength = 0;
050:
051: /**
052: * Create a HttpResponse
053: * @param exchange The exchange instance for the current HTTP transaction.
054: */
055: public HttpResponse(HttpExchange exchange)
056: //----------------------------------------
057: {
058: m_exchange = exchange;
059: m_headers = exchange.getResponseHeaders();
060: }
061:
062: /**
063: * Create a HttpResponse
064: * @param exchange The exchange instance for the current HTTP transaction.
065: * @param status The HTTP status code of the response
066: */
067: public HttpResponse(HttpExchange exchange, int status)
068: //-------------------------------------------------------
069: {
070: m_status = status;
071: m_exchange = exchange;
072: m_headers = exchange.getResponseHeaders();
073: }
074:
075: /**
076: * Create a HttpResponse
077: * @param exchange The exchange instance for the current HTTP transaction.
078: * @param status The HTTP status code of the response
079: * @param mimeType The mime type for the response
080: */
081: public HttpResponse(HttpExchange exchange, int status,
082: String mimeType)
083: //------------------------------------------------------------------
084: {
085: m_status = status;
086: m_exchange = exchange;
087: m_headers = exchange.getResponseHeaders();
088: if (mimeType != null)
089: addHeader("Content-Type", mimeType);
090:
091: }
092:
093: /**
094: * Create a HttpResponse
095: * @param exchange The exchange instance for the current HTTP transaction.
096: * @param status The HTTP status code of the response
097: * @param mimeType The mime type for the response
098: * @param body The content of the response
099: */
100: public HttpResponse(HttpExchange exchange, int status,
101: String mimeType, String body)
102: //--------------------------------------------------------------
103: {
104: m_status = status;
105: m_exchange = exchange;
106: m_headers = exchange.getResponseHeaders();
107: if (mimeType != null)
108: addHeader("Content-Type", mimeType);
109: m_body = body;
110: }
111:
112: /**
113: * Get a header value that was set for this response.
114: * @param k The header key
115: * @return The header string for key k
116: */
117: public String getHeader(String k)
118: //--------------------------------------------
119: {
120: return m_headers.getFirst(k);
121: }
122:
123: /**
124: * Get a header value that was set for this response.
125: * @param k The header key
126: * @param index If there is more than one value for the header key then
127: * return the kth value.
128: * @return The header for key k and index <i>index</i>
129: */
130: public String getHeader(String k, int index)
131: //--------------------------------------------
132: {
133: List<String> l = null;
134: try {
135: l = m_headers.get(k);
136: if (l == null)
137: return null;
138: return l.get(index);
139: } catch (ClassCastException e) {
140: return m_headers.getFirst(k);
141: } catch (IndexOutOfBoundsException e) {
142: return null;
143: }
144: }
145:
146: /**
147: * Add a header value.
148: * @param k The header key to add a value for
149: * @param v The header value
150: */
151: public void addHeader(String k, String v)
152: //---------------------------------------
153: {
154: k = k.trim();
155: if (v == null)
156: v = "";
157: List<String> l = m_headers.get(k);
158: if (l == null) {
159: l = new ArrayList<String>();
160: l.add(v);
161: m_headers.put(k, l);
162: } else
163: l.add(v);
164: }
165:
166: public void setStatus(int status) {
167: m_status = status;
168: }
169:
170: public void setBody(String body) {
171: m_body = body;
172: }
173:
174: public void setMimeType(String mimeType)
175: //---------------------------------------------
176: {
177: addHeader("Content-Type", mimeType);
178: }
179:
180: /**
181: * Send the Response headers
182: * @return true if successful otherwise false
183: */
184: public boolean sendHeaders()
185: //--------------------------
186: {
187: return sendHeaders(-1);
188: }
189:
190: /**
191: * Send the Response headers
192: * @param contentLength The content length for the body text that will be
193: * sent with sendData. If contentLength is -1 then the length of the body
194: * set in the constructor will be used
195: * @return true if successfull otherwise false
196: */
197: public boolean sendHeaders(long contentLength)
198: //-------------------------------------------
199: {
200: if (contentLength < 0)
201: m_contentLength = m_body.length();
202: else
203: m_contentLength = contentLength;
204: String date = m_headers.getFirst("Date");
205: if (date == null)
206: addHeader("Date", Http.strDate(null));
207: try {
208: if (m_contentLength >= 0) {
209: m_exchange.sendResponseHeaders(m_status,
210: m_contentLength);
211: m_responseStream = m_exchange.getResponseBody();
212: } else
213: return false;
214: } catch (Exception e) {
215: return false;
216: }
217: return true;
218: }
219:
220: /**
221: * Send the Response data
222: * @return true if Response was successfully sent otherwise false
223: */
224: public boolean sendData()
225: //-----------------------
226: {
227: if (m_responseStream == null)
228: return false;
229: if (m_body.length() == 0)
230: return false;
231: if (m_contentLength <= 0)
232: return false;
233: return sendData(m_body);
234: }
235:
236: /**
237: * Send the Response data
238: * @param s The string to send as the Response body. Note: This must be the
239: * same length as the contentLength set in sendHeaders.
240: * @return true if Response was successfully sent otherwise false
241: */
242: public boolean sendData(String s)
243: //-------------------------------
244: {
245: if (m_responseStream == null)
246: return false;
247: if (s.length() == 0)
248: return false;
249: if (s.length() != m_contentLength)
250: return false;
251:
252: InputStream data = new ByteArrayInputStream(s.getBytes());
253: try {
254: return sendData(data);
255: } finally {
256: if (data != null)
257: try {
258: data.close();
259: } catch (Exception e) {
260: }
261: if (m_responseStream != null)
262: try {
263: m_responseStream.close();
264: } catch (Exception e) {
265: }
266: }
267: }
268:
269: /**
270: * Send the Response data
271: * @param data The stream to send as the Response body. Note: This must be the
272: * same length as the contentLength set in sendHeaders.
273: * @return true if Response was successfully sent otherwise false
274: */
275: public boolean sendData(InputStream data)
276: //---------------------------------------
277: {
278: if (m_responseStream == null)
279: return false;
280: try {
281: Http.readWriteStream(data, m_responseStream);
282: return true;
283: } catch (Exception e) {
284: Httpd.Log(Httpd.LogLevel.ERROR,
285: "Error sending response data", e);
286: return false;
287: } finally {
288: try {
289: m_responseStream.close();
290: } catch (Exception e) {
291: }
292: }
293: }
294:
295: /**
296: * Send the Response using the body and/or status previously specified in the
297: * constructor. If the body is not specified then nol content is send only
298: * the headers.
299: * @return true if Response was successfully sent otherwise false
300: */
301: public boolean send()
302: //-------------------
303: {
304: if (sendHeaders(((m_body != null) && (m_body.length() > 0)) ? m_body
305: .length()
306: : -1))
307: sendData();
308: else
309: return false;
310: return true;
311: }
312:
313: /**
314: * Create a HTTP NOT FOUND response
315: * @param exchange The exchange instance for the current HTTP transaction.
316: * @param uri The URI for the response
317: * @param requestHeaders The request headers
318: * @return An HttpResponse instance
319: */
320: static public HttpResponse notFound(HttpExchange exchange, URI uri,
321: Headers requestHeaders)
322: //----------------------------------------------------------------
323: {
324: String accept = requestHeaders.getFirst("Accept");
325: boolean isHtml = accept.toLowerCase().contains("text/html");
326: HttpResponse response = null;
327: if ((isHtml)
328: && (exchange.getRequestMethod().compareToIgnoreCase(
329: "head") != 0)) {
330: String html = "<html>\n<head>\n"
331: + "<title>Error: File not found " + uri.getPath()
332: + "</title>\n" + "<body>\n<h1>File not Found<b>"
333: + "</b></h1><br>\nThe requested URL <b>"
334: + uri.toASCIIString() + " could not be located ("
335: + uri.getPath() + ")\n<hr>";
336: response = new HttpResponse(exchange, Http.HTTP_NOTFOUND,
337: Http.MIME_HTML, html);
338: } else
339: response = new HttpResponse(exchange, Http.HTTP_NOTFOUND);
340: return response;
341: }
342:
343: /**
344: * Create a HTTP REDIRECT response
345: * @param exchange The exchange instance for the current HTTP transaction.
346: * @param uri The URI for the response
347: * @param requestHeaders The request headers
348: * @return An HttpResponse instance
349: */
350: static public HttpResponse reDirect(HttpExchange exchange, URI uri,
351: Headers requestHeaders)
352: //----------------------------------------------------------------
353: {
354: HttpResponse r = null;
355: if (exchange.getRequestMethod().compareToIgnoreCase("head") != 0) {
356: String html = "<html><body>Redirected. Click this link if you "
357: + "are not redirected <a href=\""
358: + uri.toString()
359: + "\">"
360: + uri.toASCIIString()
361: + "</a></body></html>";
362: r = new HttpResponse(exchange, Http.HTTP_REDIRECT,
363: Http.MIME_HTML, html);
364: } else
365: r = new HttpResponse(exchange, Http.HTTP_REDIRECT);
366: r.addHeader("Location", uri.toASCIIString());
367: return r;
368: }
369:
370: /**
371: * Create a HTTP ACCESS DENIED response
372: * @param exchange The exchange instance for the current HTTP transaction.
373: * @param uri The URI for the response
374: * @param requestHeaders The request headers
375: * @return An HttpResponse instance
376: */
377: static public HttpResponse accessDenied(HttpExchange exchange,
378: URI uri, Headers requestHeaders)
379: //----------------------------------------------------------------
380: {
381: String accept = requestHeaders.getFirst("Accept");
382: boolean isHtml = accept.toLowerCase().contains("text/html");
383: HttpResponse response = null;
384: if ((isHtml)
385: && (exchange.getRequestMethod().compareToIgnoreCase(
386: "head") != 0)) {
387: String html = "<html>\n<head>\n"
388: + "<title>Error: Access denied: " + uri.getPath()
389: + "</title>\n" + "<body>\n<h1>Access denied<b>"
390: + "</b></h1><br>\nAccess denied to URL <b>"
391: + uri.toASCIIString() + "\n<hr>";
392: response = new HttpResponse(exchange, Http.HTTP_FORBIDDEN,
393: Http.MIME_HTML, html);
394: } else
395: response = new HttpResponse(exchange, Http.HTTP_FORBIDDEN);
396: return response;
397: }
398:
399: /**
400: * Create a HTTP INTERNAL ERROR response
401: * @param exchange The exchange instance for the current HTTP transaction.
402: * @param uri The URI for the response
403: * @param requestHeaders The request headers
404: * @return An HttpResponse instance
405: */
406: static public HttpResponse internalError(HttpExchange exchange,
407: URI uri, Headers requestHeaders)
408: //----------------------------------------------------------------
409: {
410: String accept = requestHeaders.getFirst("Accept");
411: boolean isHtml = accept.toLowerCase().contains("text/html");
412: HttpResponse response = null;
413: if ((isHtml)
414: && (exchange.getRequestMethod().compareToIgnoreCase(
415: "head") != 0)) {
416: String html = "<html>\n<head>\n"
417: + "<title>Error: Internal error " + uri.getPath()
418: + "</title>\n" + "<body>\n<h1>Internal error<b>"
419: + "</b></h1><br>\nThe requested URL <b>"
420: + uri.toASCIIString()
421: + " caused an internal error (" + uri.getPath()
422: + ")\n<hr>";
423: response = new HttpResponse(exchange,
424: Http.HTTP_INTERNALERROR, Http.MIME_HTML, html);
425: } else
426: response = new HttpResponse(exchange,
427: Http.HTTP_INTERNALERROR);
428: return response;
429: }
430:
431: /**
432: * Create a HTTP NOTIFY CONTINUE response
433: * @param exchange The exchange instance for the current HTTP transaction.
434: * @return An HttpResponse instance
435: */
436: static public HttpResponse notifyContinue(HttpExchange exchange)
437: //----------------------------------------------------------------
438: {
439: if (exchange.getProtocol().contains("1.0"))
440: return null;
441: HttpResponse response = new HttpResponse(exchange,
442: Http.HTTP_CONTINUE);
443: return response;
444: }
445: }
|