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