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