// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.http; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TimeZone; public abstract class Response { public enum Format { XML("text/xml"), HTML("text/html; charset=utf-8"), TEXT("text/text"), JSON("text/json"), JAVA("java"); private final String contentType; private Format(String contentType) { this.contentType = contentType; } public String getContentType() { return contentType; } } protected static final String CRLF = "\r\n"; public static SimpleDateFormat makeStandardHttpDateFormat() { // SimpleDateFormat is not thread safe, so we need to create each // instance independently. SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); df.setTimeZone(TimeZone.getTimeZone("GMT")); return df; } private int status = 200; private HashMap<String, String> headers = new HashMap<String, String>(17); private String contentType = Format.HTML.contentType; public Response(String formatString) { Format format; if ("html".equalsIgnoreCase(formatString)) { format = Format.HTML; } else if ("xml".equalsIgnoreCase(formatString)) { format = Format.XML; } else if ("text".equalsIgnoreCase(formatString)) { format = Format.TEXT; } else if ("java".equalsIgnoreCase(formatString)){ format = Format.JAVA; } else { format = Format.HTML; } setContentType(format.getContentType()); } public Response(String format, int status) { this(format); this.status = status; } public boolean isXmlFormat() { return Format.XML.contentType.equals(contentType); } public boolean isHtmlFormat() { return Format.HTML.contentType.equals(contentType); } public boolean isTextFormat() { return Format.TEXT.contentType.equals(contentType); } public boolean isJavaFormat(){ return Format.JAVA.contentType.equals(contentType); } public abstract void sendTo(ResponseSender sender) throws IOException; public abstract int getContentSize(); public int getStatus() { return status; } public void setStatus(int s) { status = s; } public String makeHttpHeaders() { StringBuffer text = new StringBuffer(); if (!Format.TEXT.contentType.equals(contentType)) { text.append("HTTP/1.1 ").append(status).append(" ").append( getReasonPhrase()).append(CRLF); makeHeaders(text); text.append(CRLF); } return text.toString(); } public String getContentType() { return contentType; } public void setContentType(String type) { contentType = type; } public void setContentType(Format format) { contentType = format.getContentType(); } public void redirect(String location) { status = 303; addHeader("Location", location); } public void setMaxAge(int age) { addHeader("Cache-Control", "max-age=" + age); } public void setLastModifiedHeader(String date) { addHeader("Last-Modified", date); } public void setExpiresHeader(String date) { addHeader("Expires", date); } public void addHeader(String key, String value) { headers.put(key, value); } public String getHeader(String key) { return headers.get(key); } public byte[] getEncodedBytes(String value) { // TODO: -AJM- Defer encoding to the latest responsible moment try { return value.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unable to encode data", e); } } void makeHeaders(StringBuffer text) { for (Entry<String, String> entry: headers.entrySet()) { text.append(entry.getKey()).append(": ").append(entry.getValue()).append(CRLF); } } protected void addStandardHeaders() { addHeader("Content-Type", getContentType()); } protected String getReasonPhrase() { return getReasonPhrase(status); } private static Map<Integer, String> reasonCodes = new HashMap<Integer, String>() { private static final long serialVersionUID = 1L; { put(100, "Continue"); put(101, "Switching Protocols"); put(200, "OK"); put(201, "Created"); put(202, "Accepted"); put(203, "Non-Authoritative Information"); put(204, "No Content"); put(205, "Reset Content"); put(300, "Multiple Choices"); put(301, "Moved Permanently"); put(302, "Found"); put(303, "See Other"); put(304, "Not Modified"); put(305, "Use Proxy"); put(307, "Temporary Redirect"); put(400, "Bad Request"); put(401, "Unauthorized"); put(402, "Payment Required"); put(403, "Forbidden"); put(404, "Not Found"); put(405, "Method Not Allowed"); put(406, "Not Acceptable"); put(407, "Proxy Authentication Required"); put(408, "Request Time-out"); put(409, "Conflict"); put(410, "Gone"); put(411, "Length Required"); put(412, "Precondition Failed"); put(413, "Request Entity Too Large"); put(414, "Request-URI Too Large"); put(415, "Unsupported Media Type"); put(416, "Requested range not satisfiable"); put(417, "Expectation Failed"); put(500, "Internal Server Error"); put(501, "Not Implemented"); put(502, "Bad Gateway"); put(503, "Service Unavailable"); put(504, "Gateway Time-out"); put(505, "HTTP Version not supported"); } }; public static String getReasonPhrase(int status) { String reasonPhrase = reasonCodes.get(status); return reasonPhrase == null ? "Unknown Status" : reasonPhrase; } }