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