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)); 73 } else { 74 return 0; 75 } 76 }, 77 /*length : function() { 78 return this._buffer.capacity(); 79 },*/ 80 reset : function() { 81 if (this._buffer != null) { 82 this._buffer.clear(); 83 } 84 return this; 85 }, 86 /* 87 * TODO Use jls.net.BufferSelectionHandler for R/W 88 */ 89 onRead : function(input) { 90 jls.logger.debug('onRead()'); 91 if (this._hdrStr == null) { 92 this._hdrStr = ''; 93 // clear header 94 this._startLine = ''; 95 this._fields = {}; 96 } 97 var b = input.readByte(); 98 if (b < 0) { 99 jls.logger.debug('b = ' + b); 100 return -1; // cannot read 101 } 102 for (; b >= 0; b = input.readByte()) { 103 jls.logger.debug('b = ' + b); 104 this._hdrStr += String.fromCharCode(b); 105 //jls.logger.debug('header part: ->' + this._hdrStr + '<-'); 106 if (this._hdrStr.substr(-4) == '\r\n\r\n') { 107 this.fromString(this._hdrStr); 108 this._hdrStr = null; 109 return 1; // completed 110 } 111 } 112 return 0; // in progress 113 }, 114 onWrite : function(output) { 115 if (this._buffer == null) { 116 // TODO Correct invalid charset 117 this._buffer = jls.lang.ByteBuffer.fromString(this.toString(), 'ASCII'); 118 } 119 if (output.write(this._buffer) == 0) { 120 return -1; // cannot write 121 } 122 if (this._buffer.remaining() == 0) { 123 this._buffer.clear(); 124 return 1; // completed 125 } 126 return 0; // in progress 127 }, 128 fromString : function(s) { 129 var lines = s.split(jls.net.http.HttpHeader.lineSeparator); 130 var startLine = lines[0]; 131 jls.logger.trace('set(parse) start line'); 132 this.setStartLine(startLine); 133 for (var i = 1; i < lines.length; i++) { 134 var entry = lines[i].split(/\s*:\s*/); 135 if (entry.length != 2) { 136 // TODO Throw something ? 137 continue; 138 } 139 this.setField(entry[0], entry[1]); 140 } 141 return this; 142 }, 143 toString : function() { 144 var header = this.getStartLine() + jls.net.http.HttpHeader.lineSeparator; 145 for (var k in this._fields) { 146 header += k + ': ' + this._fields[k] + jls.net.http.HttpHeader.lineSeparator; 147 } 148 header += jls.net.http.HttpHeader.lineSeparator; 149 return header; 150 } 151 }); 152 153 Object.extend(jls.net.http.HttpHeader, /** @lends jls.net.http.HttpHeader */ 154 { 155 fromString : function(s) { 156 var hdr = s.startsWith(jls.net.http.HttpHeader.VERSION_PREFIX) ? new jls.net.http.HttpResponseHeader() : new jls.net.http.HttpRequestHeader(); 157 hdr.fromString(s); 158 return hdr; 159 }, 160 lineSeparator : '\r\n', 161 //space : ' ', 162 /* 163 100 Continue 164 101 Switching Protocols 165 200 OK 166 201 Created 167 202 Accepted 168 203 Non-Authoritative Information 169 204 No Content 170 205 Reset Content 171 206 Partial Content 172 300 Multiple Choices 173 301 Moved Permanently 174 302 Moved Temporarily 175 303 See Other 176 304 Not Modified 177 305 Use Proxy 178 400 Bad Request 179 401 Unauthorized 180 402 Payment Required 181 403 Forbidden 182 404 Not Found 183 405 Method Not Allowed 184 406 Not Acceptable 185 407 Proxy Authentication Required 186 408 Request Time-out 187 409 Conflict 188 410 Gone 189 411 Length Required 190 412 Precondition Failed 191 413 Request Entity Too Large 192 414 Request-URI Too Large 193 415 Unsupported Media Type 194 500 Internal Server Error 195 501 Not Implemented 196 502 Bad Gateway 197 503 Service Unavailable 198 504 Gateway Time-out 199 505 HTTP Version not supported 200 */ 201 HTTP_OK : 200, 202 HTTP_NOT_FOUND : 404, 203 204 /* 205 OPTIONS 206 GET 207 HEAD 208 POST 209 PUT 210 DELETE 211 TRACE 212 */ 213 METHOD_GET : 'GET', 214 METHOD_POST : 'POST', 215 216 /* 217 There are two major versions, HTTP/1.0 that uses a separate connection for every document and 218 HTTP/1.1 that can reuse the same connection to download, for instance, images for the just served page. 219 Hence HTTP/1.1 may be faster as it takes time to set up such connections. 220 */ 221 VERSION_PREFIX : 'HTTP/', // TODO Rename as Protocol? 222 VERSION_1_0 : '1.0', 223 VERSION_1_1 : '1.1', 224 225 DEFAULT_USER_AGENT: 'JLS', 226 227 HEADER_HOST: 'Host', 228 HEADER_USER_AGENT: 'User-Agent', 229 HEADER_ACCEPT: 'Accept', 230 HEADER_ACCEPT_LANGUAGE: 'Accept-Language', 231 HEADER_ACCEPT_ENCODING: 'Accept-Encoding', 232 HEADER_ACCEPT_CHARSET: 'Accept-Charset', 233 HEADER_KEEP_ALIVE: 'Keep-Alive', 234 HEADER_PROXY_CONNECTION: 'Proxy-Connection', 235 HEADER_COOKIE: 'Cookie', 236 HEADER_CONTENT_LENGTH: 'Content-Length', 237 HEADER_CONTENT_TYPE: 'Content-Type' 238 239 }); 240 241