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