/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.communication.protocol.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.ws4d.java.communication.ProtocolException;
import org.ws4d.java.communication.protocol.http.header.HTTPResponseHeader;
import org.ws4d.java.constants.HTTPConstants;
import org.ws4d.java.constants.MIMEConstants;
import org.ws4d.java.html.HTMLDocument;
import org.ws4d.java.html.SimpleHTML;
import org.ws4d.java.structures.ByteArray;
import org.ws4d.java.structures.HashMap;
import org.ws4d.java.types.InternetMediaType;
import org.ws4d.java.types.URI;
import org.ws4d.java.util.Log;
/**
* Utility class for the easier creation of HTTP response messages.
*/
public class HTTPResponseUtil {
/*
* We are shy!
*/
private HTTPResponseUtil() {
}
/**
* Sends default HTTP 100 Continue response.
*
* @param out stream to work with.
* @param message message body.
*/
public static void sendCountinueResponse(OutputStream out, String message) {
sendResponse(out, 100, message);
}
/**
* Sends default HTTP 200 OK response.
*
* @param out stream to work with.
* @param message message body.
*/
public static void sendOKResponse(OutputStream out, String message) {
sendResponse(out, 200, message);
}
/**
* Sends default HTTP 200 OK response.
*
* @param out stream to work with.
* @param document message document.
*/
public static void sendOKResponse(OutputStream out, HTMLDocument document) {
sendResponse(out, 200, document);
}
/**
* Sends default HTTP 204 No Content response.
*
* @param out stream to work with.
* @param message message body.
* @return the HTTP response.
*/
public static void sendNoContentResponse(OutputStream out, String message) {
sendResponse(out, 204, message);
}
/**
* Sends default HTTP 404 Not Found response.
*
* @param out stream to work with.
* @param document message document.
*/
public static void sendNotFoundResponse(OutputStream out, HTMLDocument document) {
sendResponse(out, 404, document);
}
/**
* Sends default HTTP 404 Not Found response.
*
* @param out stream to work with.
* @param message message body.
*/
public static void sendNotFoundResponse(OutputStream out, String message) {
sendResponse(out, 404, message);
}
/**
* Sends a byte array with correct HTTP response.
*
* @param out stream to work with.
* @param status HTTP status code for the response.
* @param message message body.
*/
public static void sendResponse(OutputStream out, int status, String message) {
// create response header
HTTPResponseHeader header = getResponseHeader(status);
String defaultContentType = MIMEConstants.MEDIATYPE_TEXT + MIMEConstants.SEPARATOR + MIMEConstants.SUBTYPE_PLAIN;
// compute message
int ml = 0;
byte[] messageData = null;
if (message != null) {
messageData = message.getBytes();
ml = messageData.length;
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH, String.valueOf(ml));
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_TYPE, defaultContentType);
}
sendResponse(out, messageData, header);
}
/**
* Sends a byte array with correct HTTP response.
*
* @param out stream to work with.
* @param status HTTP status code for the response.
* @param message message body.
*/
public static void sendResponse(OutputStream out, int status, HTMLDocument message) {
// create response header
HTTPResponseHeader header = getResponseHeader(status);
String defaultContentType = MIMEConstants.MEDIATYPE_TEXT + MIMEConstants.SEPARATOR + MIMEConstants.SUBTYPE_HTML;
// compute message
int ml = 0;
byte[] messageData = null;
if (message != null) {
messageData = message.getData();
ml = messageData.length;
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH, String.valueOf(ml));
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_TYPE, defaultContentType);
}
sendResponse(out, messageData, header);
}
/**
* Sends a byte array with correct HTTP response.
*
* @param out stream to work with.
* @param message message body.
* @param header the HTTP response header. Please set the correct content
* length etc.
*/
public static void sendResponse(OutputStream out, byte[] message, HTTPResponseHeader header) {
try {
header.toStream(out);
if (message != null) {
out.write(message);
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Sends an HTTP bad request.
*
* @param out stream to work with.
* @param note the error note.
*/
public static void sendBadRequest(OutputStream out, String note) {
// create response header
HTTPResponseHeader header = getResponseHeader(400);
try {
header.toStream(out);
if (note != null) {
out.write(note.getBytes());
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Sends an HTTP bad request.
*
* @param out stream to work with.
* @param note the error note.
*/
public static void sendInternalServerError(OutputStream out, String note) {
// create response header
HTTPResponseHeader header = getResponseHeader(500);
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONNECTION, HTTPConstants.HTTP_HEADERVALUE_CONNECTION_CLOSE);
try {
header.toStream(out);
if (note != null) {
out.write(note.getBytes());
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Sends an HTTP version not supported.
*
* @param out stream to work with.
* @param note the error note.
*/
public static void sendHTTPVersionNotSupported(OutputStream out, String note) {
// create response header
HTTPResponseHeader header = getResponseHeader(505);
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONNECTION, HTTPConstants.HTTP_HEADERVALUE_CONNECTION_CLOSE);
try {
header.toStream(out);
if (note != null) {
out.write(note.getBytes());
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Sends an HTTP unsupported media type
*
* @param out stream to work with.
* @param note the error note.
*/
public static void sendUnsupportedMediaType(OutputStream out, String note) {
// create response header
HTTPResponseHeader header = getResponseHeader(415);
try {
header.toStream(out);
if (note != null) {
out.write(note.getBytes());
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Sends a HTTP redirect.
*
* @param out stream to work with.
* @param request the request which was done.
* @param note the error note.
*/
public static void sendRedirect(OutputStream out, URI request, String note) {
// create response header
HTTPResponseHeader header = getResponseHeader(307);
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_LOCATION, request.getPath());
try {
header.toStream(out);
if (note != null) {
out.write(note.getBytes());
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Sends the default Error document.
*
* @param out stream to work with.
* @param request the request which was done.
*/
public static void sendDefaultErrorDocument(OutputStream out, String request) {
SimpleHTML html = new SimpleHTML("Not Found");
html.addParagraph("The requested URI " + request + " was not found on this server.");
html.addHorizontalRule();
html.addParagraph("<i>Java Multi Edition DPWS Framework</i>");
sendNotFoundResponse(out, html);
}
/**
* Sends the default document.
*
* @param out stream to work with.
*/
public static void sendDefaultDocument(OutputStream out) {
SimpleHTML html = new SimpleHTML("It works!");
sendOKResponse(out, html);
}
/**
* Creates HTTP 204 "No Content" Header.
*
* @return the HTTP response header.
*/
public static HTTPResponseHeader getResponseHeader() {
return getResponseHeader(204);
}
/**
* Returns the default HTTP response header for the given status code.
*
* @param status the status code.
* @return the HTTP response header.
*/
public static HTTPResponseHeader getResponseHeader(int status) {
String version = HTTPConstants.HTTP_VERSION11;
String phrase = null;
switch (status) {
case 100:
phrase = HTTPStatus.HTTP_100;
break;
case 200:
phrase = HTTPStatus.HTTP_200;
break;
case 202:
phrase = HTTPStatus.HTTP_202;
break;
case 204:
phrase = HTTPStatus.HTTP_204;
break;
case 300:
phrase = HTTPStatus.HTTP_300;
break;
case 301:
phrase = HTTPStatus.HTTP_301;
break;
case 302:
phrase = HTTPStatus.HTTP_302;
break;
case 303:
phrase = HTTPStatus.HTTP_303;
break;
case 304:
phrase = HTTPStatus.HTTP_304;
break;
case 307:
phrase = HTTPStatus.HTTP_307;
break;
case 400:
phrase = HTTPStatus.HTTP_400;
break;
case 401:
phrase = HTTPStatus.HTTP_401;
break;
case 403:
phrase = HTTPStatus.HTTP_403;
break;
case 404:
phrase = HTTPStatus.HTTP_404;
break;
case 415:
phrase = HTTPStatus.HTTP_415;
break;
case 500:
phrase = HTTPStatus.HTTP_500;
break;
case 501:
phrase = HTTPStatus.HTTP_501;
break;
case 505:
phrase = HTTPStatus.HTTP_505;
break;
}
return new HTTPResponseHeader(version, status, phrase);
}
public static HTTPResponseHeader handleResponse(InputStream in) throws IOException, ProtocolException {
String version = null;
String status = null;
String reason = null;
version = HTTPUtil.readElement(in);
// if (!version.equals(HTTPConstants.HTTP_VERSION11)) {
// throw new ProtocolException("Unsupported HTTP version.");
// }
status = HTTPUtil.readElement(in);
reason = HTTPUtil.readRequestLine(in);
// Read the HTTP header fields
HashMap headerfields = new HashMap();
HTTPUtil.readHeaderFields(in, headerfields);
int s = 0;
try {
s = Integer.valueOf(status).intValue();
} catch (NumberFormatException e) {
throw new IOException("Cannot determinate HTTP version.");
}
return new HTTPResponseHeader(version, s, reason, headerfields);
}
/**
* Writes an HTTP response header to the stream with given media type (e.g.
* application/soap+xml). Can be set to chunked mode if the length of
* followed communication cannot be determined. The returned
* <code>OutputStream</code> MUST be used it should be ensured that the
* chunks are written correctly.
*
* @param out the output stream to write the HTTP request to.
* @param code the HTTP response code.
* @param type the internet media type.
* @param chunked <code>true</code> if a special chunked output stream
* should be returned, <code>false</code> otherwise.
* @param trailer <code>true</code> if the chunk trailer should be appended
* at the end, <code>false</code> otherwise.
* @return <code>ChunkedOutputStream</code> if <code>chunked</code> is true,
* the normal output stream otherwise.
* @throws IOException
*/
public static OutputStream writeResponse(OutputStream out, int code, InternetMediaType type, boolean chunked, boolean trailer) throws IOException {
HTTPResponseHeader header = HTTPResponseUtil.getResponseHeader(code);
if (Log.isDebug()) {
Log.debug("<O> " + header.toString(), Log.DEBUG_LAYER_COMMUNICATION);
}
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_TYPE, type.toString());
if (chunked) {
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_TRANSFER_ENCODING, HTTPConstants.HTTP_HEADERVALUE_TRANSFERCODING_CHUNKED);
header.toStream(out);
return new ChunkedOutputStream(out, trailer);
}
header.toStream(out);
return out;
}
/**
* Sends the resource.
*
* @param out stream to work with.
* @param res resource to send.
* @param type <code>MIME</code> type for this resource.
* @param chunked if <code>false</code> the whole resource is loaded into
* memory before it is sent. if <code>true</code> the resource is
* sent as chunked response, with out much memory usage.
* @param trailer <code>true</code> if you want to send the OPTIONAL chunk
* trailer, <code>false</code> otherwise.
* @return <code>true</code> if the resource could be loaded and could be
* sent, <code>false</code> otherwise.
*/
public static boolean sendResource(OutputStream out, String res, InternetMediaType type, boolean chunked, boolean trailer) {
InputStream resIn = out.getClass().getResourceAsStream(res);
if (resIn == null) {
return false;
}
try {
out = writeResponse(out, 200, type, chunked, trailer);
if (chunked) {
int i = -1;
if (Log.isDebug()) {
Log.debug("Sending chunked resource [ " + res + " ] over HTTP.", Log.DEBUG_LAYER_COMMUNICATION);
}
while (resIn.available() > 0 && (i = resIn.read()) != -1) {
out.write(i);
}
// out.flush();
} else {
if (Log.isDebug()) {
Log.debug("Sending resource [ " + res + " ] over HTTP.", Log.DEBUG_LAYER_COMMUNICATION);
}
// create response header
HTTPResponseHeader header = getResponseHeader(200);
String defaultContentType = type.getMediaType();
// compute message
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_TYPE, defaultContentType);
// load resource into memory
int i = -1;
ByteArray buffer = new ByteArray();
while (resIn.available() > 0 && (i = resIn.read()) != -1) {
buffer.append((byte) i);
}
// now we know the length
header.addHeaderFieldValue(HTTPConstants.HTTP_HEADER_CONTENT_LENGTH, String.valueOf(buffer.size()));
// write HTTP header
header.toStream(out);
// out.flush();
// write HTTP body
out.write(buffer.getBytes());
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.printStackTrace(e);
} finally {
try {
resIn.close();
} catch (IOException e) {
Log.printStackTrace(e);
}
}
return true;
}
}