/src/kilim/http/HttpResponse.java

http://github.com/kilim/kilim · Java · 242 lines · 188 code · 34 blank · 20 comment · 19 complexity · f69aa7cffeb2017b723e03de43c6703f MD5 · raw file

  1. /* Copyright (c) 2006, Sriram Srinivasan
  2. *
  3. * You may distribute this software under the terms of the license
  4. * specified in the file "License"
  5. */
  6. package kilim.http;
  7. import java.io.DataOutputStream;
  8. import java.io.IOException;
  9. import java.io.OutputStream;
  10. import java.nio.ByteBuffer;
  11. import java.text.SimpleDateFormat;
  12. import java.util.ArrayList;
  13. import java.util.Date;
  14. import java.util.TimeZone;
  15. import java.util.concurrent.ConcurrentHashMap;
  16. import kilim.Constants;
  17. import kilim.Pausable;
  18. import kilim.nio.EndPoint;
  19. import kilim.nio.ExposedBaos;
  20. /**
  21. * The response object encapsulates the header and often, but not always, the content. The caller must set all the
  22. * fields, except for the protocol, server and date. The body of the response (the content) is written to a stream
  23. * obtained from {@link #getOutputStream()}.
  24. */
  25. public class HttpResponse extends HttpMsg {
  26. // Status codes
  27. public static final byte[] ST_CONTINUE = "100 Continue\r\n".getBytes();
  28. public static final byte[] ST_SWITCHING_PROTOCOLS = "101 Switching Protocols\r\n"
  29. .getBytes();
  30. // Successful status codes
  31. public static final byte[] ST_OK = "200 OK\r\n".getBytes();
  32. public static final byte[] ST_CREATED = "201 Created\r\n".getBytes();
  33. public static final byte[] ST_ACCEPTED = "202 Accepted\r\n".getBytes();
  34. public static final byte[] ST_NON_AUTHORITATIVE = "203 Non-Authoritative Information\r\n"
  35. .getBytes();
  36. public static final byte[] ST_NO_CONTENT = "204 No Content\r\n".getBytes();
  37. public static final byte[] ST_RESET_CONTENT = "205 Reset Content\r\n"
  38. .getBytes();
  39. public static final byte[] ST_PARTIAL_CONTENT = "206 Partial Content\r\n"
  40. .getBytes();
  41. // Redirection status codes
  42. public static final byte[] ST_MULTIPLE_CHOICES = "300 Multiple Choices\r\n"
  43. .getBytes();
  44. public static final byte[] ST_MOVED_PERMANENTLY = "301 Moved Permanently\r\n"
  45. .getBytes();
  46. public static final byte[] ST_FOUND = "302 Found\r\n".getBytes();
  47. public static final byte[] ST_SEE_OTHER = "303 See Other\r\n".getBytes();
  48. public static final byte[] ST_NOT_MODIFIED = "304 Not Modified\r\n"
  49. .getBytes();
  50. public static final byte[] ST_USE_PROXY = "305 Use Proxy\r\n".getBytes();
  51. public static final byte[] ST_TEMPORARY_REDIRECT = "307 Temporary Redirect\r\n"
  52. .getBytes();
  53. // Client error codes
  54. public static final byte[] ST_BAD_REQUEST = "400 Bad Request\r\n".getBytes();
  55. public static final byte[] ST_UNAUTHORIZED = "401 Unauthorized\r\n"
  56. .getBytes();
  57. public static final byte[] ST_PAYMENT_REQUIRED = "402 Payment Required\r\n"
  58. .getBytes();
  59. public static final byte[] ST_FORBIDDEN = "403 Forbidden\r\n".getBytes();
  60. public static final byte[] ST_NOT_FOUND = "404 Not Found\r\n".getBytes();
  61. public static final byte[] ST_METHOD_NOT_ALLOWED = "405 Method Not Allowed\r\n"
  62. .getBytes();
  63. public static final byte[] ST_NOT_ACCEPTABLE = "406 Not Acceptable\r\n"
  64. .getBytes();
  65. public static final byte[] ST_PROXY_AUTHENTICATION_REQUIRED = "407 Proxy Authentication Required\r\n"
  66. .getBytes();
  67. public static final byte[] ST_REQUEST_TIMEOUT = "408 Request Time-out\r\n"
  68. .getBytes();
  69. public static final byte[] ST_CONFLICT = "409 Conflict\r\n".getBytes();
  70. public static final byte[] ST_GONE = "410 Gone\r\n".getBytes();
  71. public static final byte[] ST_LENGTH_REQUIRED = "411 Length Required\r\n"
  72. .getBytes();
  73. public static final byte[] ST_PRECONDITION_FAILED = "412 Precondition Failed\r\n"
  74. .getBytes();
  75. public static final byte[] ST_REQUEST_ENTITY_TOO_LARGE = "413 Request Entity Too Large\r\n"
  76. .getBytes();
  77. public static final byte[] ST_REQUEST_URI_TOO_LONG = "414 Request-URI Too Large\r\n"
  78. .getBytes();
  79. public static final byte[] ST_UNSUPPORTED_MEDIA_TYPE = "415 Unsupported Media Type\r\n"
  80. .getBytes();
  81. public static final byte[] ST_REQUEST_RANGE_NOT_SATISFIABLE = "416 Requested range not satisfiable\r\n"
  82. .getBytes();
  83. public static final byte[] ST_EXPECTATION_FAILED = "417 Expectation Failed\r\n"
  84. .getBytes();
  85. public static final byte[] ST_TEAPOT = "418 I'm a Teapot\r\n"
  86. .getBytes();
  87. // Server error codes
  88. public static final byte[] ST_INTERNAL_SERVER_ERROR = "500 Internal Server Error\r\n"
  89. .getBytes();
  90. public static final byte[] ST_NOT_IMPLEMENTED = "501 Not Implemented\r\n"
  91. .getBytes();
  92. public static final byte[] ST_BAD_GATEWAY = "502 Bad Gateway\r\n".getBytes();
  93. public static final byte[] ST_SERVICE_UNAVAILABLE = "503 Service Unavailable\r\n"
  94. .getBytes();
  95. public static final byte[] ST_GATEWAY_TIMEOUT = "504 Gateway Time-out\r\n"
  96. .getBytes();
  97. public static final byte[] ST_HTTP_VERSION_NOT_SUPPORTED = "505 HTTP Version not supported\r\n"
  98. .getBytes();
  99. // Http response components
  100. public static final byte[] PROTOCOL = "HTTP/1.1 ".getBytes();
  101. public static final byte[] F_SERVER = ("Server: kilim "
  102. + Constants.KILIM_VERSION + "\r\n")
  103. .getBytes();
  104. public static final byte[] F_DATE = "Date: ".getBytes();
  105. public static final byte[] CRLF = "\r\n".getBytes();
  106. public static final byte[] FIELD_SEP = ": ".getBytes();
  107. public static ConcurrentHashMap<String, byte[]> byteCache = new ConcurrentHashMap<String, byte[]>();
  108. /**
  109. * The status line for the response. Can use any of the predefined strings in HttpResponse.ST_*.
  110. */
  111. public byte[] status;
  112. public ArrayList<String> keys = new ArrayList<String>();
  113. public ArrayList<String> values = new ArrayList<String>();
  114. public ExposedBaos bodyStream;
  115. public static final SimpleDateFormat gmtdf;
  116. static {
  117. gmtdf = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss");
  118. gmtdf.setTimeZone(TimeZone.getTimeZone("GMT:00"));
  119. }
  120. public HttpResponse() {
  121. this(ST_OK);
  122. }
  123. public HttpResponse(byte[] statusb) {
  124. status = statusb;
  125. }
  126. public void reuse() {
  127. status = ST_OK;
  128. keys.clear();
  129. values.clear();
  130. if (bodyStream != null) {
  131. bodyStream.reset();
  132. }
  133. if (buffer != null) {
  134. buffer.clear();
  135. }
  136. }
  137. public void setStatus(String statusMsg) {
  138. if (!statusMsg.endsWith("\r\n")) {
  139. statusMsg = statusMsg + "\r\n";
  140. }
  141. this.status = statusMsg.getBytes();
  142. }
  143. public HttpResponse(String statusMsg) {
  144. this();
  145. setStatus(statusMsg);
  146. }
  147. public HttpResponse addField(String key, String value) {
  148. keys.add(key);
  149. values.add(value);
  150. return this;
  151. }
  152. public String getHeaderValue(String key) {
  153. int nfields = keys.size();
  154. for (int i = 0; i < nfields; i++) {
  155. if (key.equals(keys.get(i))) return values.get(i);
  156. }
  157. return null;
  158. }
  159. public void writeHeader(OutputStream os) throws IOException {
  160. DataOutputStream dos = new DataOutputStream(os);
  161. dos.write(PROTOCOL);
  162. dos.write(status);
  163. dos.write(F_DATE);
  164. byte[] date = gmtdf.format(new Date()).getBytes();
  165. dos.write(date);
  166. dos.write(CRLF);
  167. dos.write(F_SERVER);
  168. if (bodyStream != null && getHeaderValue("Content-Length") == null) {
  169. setContentLength(bodyStream.size());
  170. }
  171. // Fields.
  172. int nfields = keys.size();
  173. for (int i = 0; i < nfields; i++) {
  174. String key = keys.get(i);
  175. byte[] keyb = byteCache.get(key);
  176. if (keyb == null) {
  177. keyb = key.getBytes();
  178. byteCache.put(key, keyb);
  179. }
  180. dos.write(keyb);
  181. dos.write(FIELD_SEP);
  182. dos.write(values.get(i).getBytes());
  183. dos.write(CRLF);
  184. }
  185. dos.write(CRLF);
  186. }
  187. public OutputStream getOutputStream() {
  188. if (bodyStream == null)
  189. bodyStream = new ExposedBaos(2048);
  190. return bodyStream;
  191. }
  192. public void writeTo(EndPoint endpoint) throws IOException, Pausable {
  193. ExposedBaos headerStream = new ExposedBaos();
  194. writeHeader(headerStream);
  195. ByteBuffer bb = headerStream.toByteBuffer();
  196. endpoint.write(bb);
  197. if (bodyStream != null && bodyStream.size() > 0) {
  198. bb = bodyStream.toByteBuffer();
  199. endpoint.write(bb);
  200. }
  201. }
  202. public void setContentLength(long length) {
  203. addField("Content-Length", Long.toString(length));
  204. }
  205. public void setContentType(String contentType) {
  206. addField("Content-Type", contentType);
  207. }
  208. }