// 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.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.TimeZone; import util.FileUtil; public abstract class Response { public enum Format { XML("text/xml"), HTML("text/html; charset=" + FileUtil.CHARENCODING), TEXT("text/text"), JSON("application/json"), JUNIT("text/junit"); 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<>(17); private String contentType = Format.HTML.contentType; private boolean withHttpHeaders = true; public Response(String formatString) { Format format; if ("html".equalsIgnoreCase(formatString)) { format = Format.HTML; } else if ("xml".equalsIgnoreCase(formatString)) { format = Format.XML; } else if ("junit".equalsIgnoreCase(formatString)) { format = Format.JUNIT; } else if ("text".equalsIgnoreCase(formatString)) { format = Format.TEXT; } 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 isJunitFormat() { return Format.JUNIT.contentType.equals(contentType); } public boolean hasContent() { return contentType != null; } 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 void withoutHttpHeaders() { this.withHttpHeaders = false; } public final String makeHttpHeaders() { if (!withHttpHeaders) return ""; if (hasContent()) { addContentHeaders(); } 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; } private void noContent() { contentType = null; } public void setContentType(Format format) { contentType = format.getContentType(); } public void redirect(String contextRoot, String location) { status = 303; addHeader("Location", contextRoot + location); } public void notModified(Date lastModified, Date date) { status = 304; noContent(); SimpleDateFormat httpDateFormat = makeStandardHttpDateFormat(); addHeader("Date", httpDateFormat.format(date)); setLastModifiedHeader(lastModified); addHeader("Cache-Control", "private"); } public void setMaxAge(int age) { addHeader("Cache-Control", "max-age=" + age); } public void setLastModifiedHeader(Date date) { addHeader("Last-Modified", makeStandardHttpDateFormat().format(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) throws UnsupportedEncodingException { return value.getBytes(FileUtil.CHARENCODING); } void makeHeaders(StringBuffer text) { for (Entry<String, String> entry: headers.entrySet()) { text.append(entry.getKey()).append(": ").append(entry.getValue()).append(CRLF); } } protected void addContentHeaders() { addHeader("Content-Type", getContentType()); } protected String getReasonPhrase() { return getReasonPhrase(status); } private static final 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, "SlimExpectation 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; } }