1 jls.loader.provide('jls.net.http.HttpHeader'); 2 3 jls.loader.require('jls.lang.ByteBuffer'); 4 jls.loader.require('jls.net.URL'); 5 jls.loader.require('jls.net.SelectionHandler'); 6 7 /* 8 see http://tools.ietf.org/html/rfc2068 9 10 HTTP-message = Request | Response ; HTTP/1.1 messages 11 generic-message = start-line 12 *message-header 13 CRLF 14 [ message-body ] 15 start-line = Request-Line | Status-Line 16 17 If a Content-Length header field (section 14.14) is present, its 18 value in bytes represents the length of the message-body. 19 20 two classes request - response that can write - read http 21 a handler is called when read is done 22 */ 23 24 jls.net.http.HttpHeader = jls.lang.Class.create(jls.net.SelectionHandler, /** @lends jls.net.http.HttpHeader.prototype */ 25 { 26 /** 27 * Creates an HTTP header. 28 * 29 * @param {String} [startLine] The start line. 30 * @param {Object} [fields] The fields. 31 * @constructs 32 * @augments jls.net.SelectionHandler 33 * @class This class represents an HTTP header. 34 */ 35 initialize : function(startLine, fields) { 36 this._startLine = ''; 37 this._fields = {}; 38 if (startLine) { 39 this.setStartLine(startLine); 40 } 41 if (fields) { 42 for (var k in fields) { 43 this.setField(k, fields[k]); 44 } 45 } 46 this._hdrStr = null; 47 this._buffer = null; 48 }, 49 clear : function() { 50 this._startLine = ''; 51 this._fields = {}; 52 }, 53 getStartLine : function() { 54 return this._startLine; 55 }, 56 setStartLine : function(startLine) { 57 this._startLine = startLine; 58 return this; 59 }, 60 hasField : function(key) { 61 return key in this._fields; 62 }, 63 getField : function(key) { 64 return this._fields[key]; 65 }, 66 setField : function(key, value) { 67 this._fields[key] = value; 68 return this; 69 }, 70 getContentLength : function() { 71 if (this.hasField(jls.net.http.HttpHeader.HEADER_CONTENT_LENGTH)) { 72 return parseInt(this.getField(jls.net.http.HttpHeader.HEADER_CONTENT_LENGTH), 10); 73 } else { 74 return 0; 75 } 76 }, 77 setContentLength : function(length) { 78 this.setField(jls.net.http.HttpHeader.HEADER_CONTENT_LENGTH, length); 79 }, 80 /*length : function() { 81 return this._buffer.capacity(); 82 },*/ 83 reset : function() { 84 if (this._buffer != null) { 85 this._buffer.clear(); 86 } 87 return this; 88 }, 89 /* 90 * TODO Use jls.net.BufferSelectionHandler for R/W 91 */ 92 onRead : function(input) { 93 jls.logger.debug('onRead()'); 94 if (this._hdrStr == null) { 95 this._hdrStr = ''; 96 // clear header 97 this._startLine = ''; 98 this._fields = {}; 99 } 100 var b = input.readByte(); 101 if (b < 0) { 102 jls.logger.debug('b = ' + b); 103 return -1; // cannot read 104 } 105 for (; b >= 0; b = input.readByte()) { 106 jls.logger.debug('b = ' + b); 107 this._hdrStr += String.fromCharCode(b); 108 //jls.logger.debug('header part: ->' + this._hdrStr + '<-'); 109 if (this._hdrStr.substr(-4) == '\r\n\r\n') { 110 this.fromString(this._hdrStr); 111 this._hdrStr = null; 112 return 1; // completed 113 } 114 } 115 return 0; // in progress 116 }, 117 onWrite : function(output) { 118 if (this._buffer == null) { 119 // TODO Correct invalid charset 120 this._buffer = jls.lang.ByteBuffer.fromString(this.toString(), 'ASCII'); 121 } 122 if (output.write(this._buffer) == 0) { 123 return -1; // cannot write 124 } 125 if (this._buffer.remaining() == 0) { 126 this._buffer.clear(); 127 return 1; // completed 128 } 129 return 0; // in progress 130 }, 131 fromString : function(s) { 132 var lines = s.split(jls.net.http.HttpHeader.lineSeparator); 133 var startLine = lines[0]; 134 jls.logger.trace('set(parse) start line'); 135 this.setStartLine(startLine); 136 for (var i = 1; i < lines.length; i++) { 137 var entry = lines[i].split(/\s*:\s*/); 138 if (entry.length != 2) { 139 // TODO Throw something ? 140 continue; 141 } 142 this.setField(entry[0], entry[1]); 143 } 144 return this; 145 }, 146 toString : function() { 147 var header = this.getStartLine() + jls.net.http.HttpHeader.lineSeparator; 148 for (var k in this._fields) { 149 header += k + ': ' + this._fields[k] + jls.net.http.HttpHeader.lineSeparator; 150 } 151 header += jls.net.http.HttpHeader.lineSeparator; 152 return header; 153 } 154 }); 155 156 Object.extend(jls.net.http.HttpHeader, /** @lends jls.net.http.HttpHeader */ 157 { 158 fromString : function(s) { 159 var hdr = s.startsWith(jls.net.http.HttpHeader.VERSION_PREFIX) ? new jls.net.http.HttpResponseHeader() : new jls.net.http.HttpRequestHeader(); 160 hdr.fromString(s); 161 return hdr; 162 }, 163 lineSeparator : '\r\n', 164 //space : ' ', 165 /* 166 100 Continue 167 101 Switching Protocols 168 200 OK 169 201 Created 170 202 Accepted 171 203 Non-Authoritative Information 172 204 No Content 173 205 Reset Content 174 206 Partial Content 175 300 Multiple Choices 176 301 Moved Permanently 177 302 Moved Temporarily 178 303 See Other 179 304 Not Modified 180 305 Use Proxy 181 400 Bad Request 182 401 Unauthorized 183 402 Payment Required 184 403 Forbidden 185 404 Not Found 186 405 Method Not Allowed 187 406 Not Acceptable 188 407 Proxy Authentication Required 189 408 Request Time-out 190 409 Conflict 191 410 Gone 192 411 Length Required 193 412 Precondition Failed 194 413 Request Entity Too Large 195 414 Request-URI Too Large 196 415 Unsupported Media Type 197 500 Internal Server Error 198 501 Not Implemented 199 502 Bad Gateway 200 503 Service Unavailable 201 504 Gateway Time-out 202 505 HTTP Version not supported 203 */ 204 HTTP_OK : 200, 205 HTTP_NOT_FOUND : 404, 206 207 /* 208 OPTIONS 209 GET 210 HEAD 211 POST 212 PUT 213 DELETE 214 TRACE 215 */ 216 METHOD_GET : 'GET', 217 METHOD_POST : 'POST', 218 219 /* 220 There are two major versions, HTTP/1.0 that uses a separate connection for every document and 221 HTTP/1.1 that can reuse the same connection to download, for instance, images for the just served page. 222 Hence HTTP/1.1 may be faster as it takes time to set up such connections. 223 */ 224 VERSION_PREFIX : 'HTTP/', // TODO Rename as Protocol? 225 VERSION_1_0 : '1.0', 226 VERSION_1_1 : '1.1', 227 228 DEFAULT_USER_AGENT: 'JLS', 229 230 HEADER_HOST: 'Host', 231 HEADER_USER_AGENT: 'User-Agent', 232 HEADER_ACCEPT: 'Accept', 233 HEADER_ACCEPT_LANGUAGE: 'Accept-Language', 234 HEADER_ACCEPT_ENCODING: 'Accept-Encoding', 235 HEADER_ACCEPT_CHARSET: 'Accept-Charset', 236 HEADER_KEEP_ALIVE: 'Keep-Alive', 237 HEADER_PROXY_CONNECTION: 'Proxy-Connection', 238 HEADER_COOKIE: 'Cookie', 239 HEADER_CONTENT_LENGTH: 'Content-Length', 240 HEADER_CONTENT_TYPE: 'Content-Type' 241 242 }); 243 244