selenium-webdriver 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. data/chrome/prebuilt/Win32/Release/npchromedriver.dll +0 -0
  2. data/chrome/prebuilt/x64/Release/npchromedriver.dll +0 -0
  3. data/chrome/src/extension/background.js +64 -48
  4. data/chrome/src/extension/content_script.js +253 -132
  5. data/chrome/src/extension/manifest-nonwin.json +1 -1
  6. data/chrome/src/extension/manifest-win.json +1 -1
  7. data/chrome/src/extension/utils.js +8 -8
  8. data/chrome/src/rb/lib/selenium/webdriver/chrome.rb +9 -0
  9. data/chrome/src/rb/lib/selenium/webdriver/chrome/bridge.rb +38 -280
  10. data/chrome/src/rb/lib/selenium/webdriver/chrome/command_executor.rb +119 -117
  11. data/chrome/src/rb/lib/selenium/webdriver/chrome/launcher.rb +36 -26
  12. data/common/src/js/abstractcommandprocessor.js +9 -11
  13. data/common/src/js/command.js +159 -83
  14. data/common/src/js/core/RemoteRunner.html +2 -2
  15. data/common/src/js/core/TestRunner-splash.html +3 -3
  16. data/common/src/js/core/TestRunner.html +5 -17
  17. data/common/src/js/core/scripts/htmlutils.js +4208 -2506
  18. data/common/src/js/core/scripts/selenium-api.js +2 -2
  19. data/common/src/js/core/scripts/selenium-browserbot.js +66 -58
  20. data/common/src/js/core/scripts/selenium-version.js +1 -1
  21. data/common/src/js/localcommandprocessor.js +5 -19
  22. data/common/src/js/testcase.js +2 -0
  23. data/common/src/js/webdriver.js +63 -93
  24. data/common/src/js/webelement.js +40 -42
  25. data/common/src/rb/lib/selenium/webdriver.rb +23 -14
  26. data/common/src/rb/lib/selenium/webdriver/bridge_helper.rb +8 -35
  27. data/common/src/rb/lib/selenium/webdriver/child_process.rb +2 -0
  28. data/common/src/rb/lib/selenium/webdriver/core_ext/dir.rb +1 -0
  29. data/common/src/rb/lib/selenium/webdriver/core_ext/string.rb +5 -0
  30. data/common/src/rb/lib/selenium/webdriver/driver.rb +20 -15
  31. data/common/src/rb/lib/selenium/webdriver/driver_extensions/takes_screenshot.rb +7 -2
  32. data/common/src/rb/lib/selenium/webdriver/element.rb +11 -2
  33. data/common/src/rb/lib/selenium/webdriver/error.rb +9 -5
  34. data/common/src/rb/lib/selenium/webdriver/keys.rb +1 -2
  35. data/common/src/rb/lib/selenium/webdriver/navigation.rb +16 -0
  36. data/common/src/rb/lib/selenium/webdriver/options.rb +32 -0
  37. data/common/src/rb/lib/selenium/webdriver/platform.rb +17 -1
  38. data/firefox/prebuilt/Win32/Release/webdriver-firefox.dll +0 -0
  39. data/firefox/src/extension/components/dispatcher.js +492 -0
  40. data/firefox/src/extension/components/driver-component.js +4 -1
  41. data/firefox/src/extension/components/errorcode.js +70 -0
  42. data/firefox/src/extension/components/firefoxDriver.js +173 -154
  43. data/firefox/src/extension/components/nsCommandProcessor.js +171 -132
  44. data/firefox/src/extension/components/promptService.js +5 -5
  45. data/firefox/src/extension/components/request.js +219 -0
  46. data/firefox/src/extension/components/response.js +276 -0
  47. data/firefox/src/extension/components/session.js +281 -0
  48. data/firefox/src/extension/components/sessionstore.js +226 -0
  49. data/firefox/src/extension/components/socketListener.js +350 -100
  50. data/firefox/src/extension/components/utils.js +166 -98
  51. data/firefox/src/extension/components/webdriverserver.js +9 -5
  52. data/firefox/src/extension/components/wrappedElement.js +189 -166
  53. data/firefox/src/extension/install.rdf +1 -1
  54. data/firefox/src/rb/lib/selenium/webdriver/firefox.rb +2 -0
  55. data/firefox/src/rb/lib/selenium/webdriver/firefox/binary.rb +39 -33
  56. data/firefox/src/rb/lib/selenium/webdriver/firefox/bridge.rb +7 -421
  57. data/firefox/src/rb/lib/selenium/webdriver/firefox/extension_connection.rb +7 -64
  58. data/firefox/src/rb/lib/selenium/webdriver/firefox/launcher.rb +2 -3
  59. data/firefox/src/rb/lib/selenium/webdriver/firefox/profile.rb +54 -10
  60. data/firefox/src/rb/lib/selenium/webdriver/firefox/profiles_ini.rb +2 -0
  61. data/firefox/src/rb/lib/selenium/webdriver/firefox/util.rb +6 -0
  62. data/jobbie/prebuilt/Win32/Release/InternetExplorerDriver.dll +0 -0
  63. data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
  64. data/jobbie/src/rb/lib/selenium/webdriver/ie.rb +2 -0
  65. data/jobbie/src/rb/lib/selenium/webdriver/ie/bridge.rb +38 -13
  66. data/jobbie/src/rb/lib/selenium/webdriver/ie/lib.rb +9 -2
  67. data/jobbie/src/rb/lib/selenium/webdriver/ie/util.rb +5 -0
  68. data/remote/client/src/rb/lib/selenium/webdriver/remote.rb +2 -0
  69. data/remote/client/src/rb/lib/selenium/webdriver/remote/bridge.rb +42 -38
  70. data/remote/client/src/rb/lib/selenium/webdriver/remote/commands.rb +56 -47
  71. data/remote/client/src/rb/lib/selenium/webdriver/remote/default_http_client.rb +26 -26
  72. data/remote/client/src/rb/lib/selenium/webdriver/remote/patron_http_client.rb +58 -0
  73. data/remote/client/src/rb/lib/selenium/webdriver/remote/response.rb +10 -12
  74. data/remote/client/src/rb/lib/selenium/webdriver/remote/server_error.rb +2 -17
  75. metadata +44 -23
  76. data/common/src/js/context.js +0 -58
  77. data/firefox/src/extension/components/context.js +0 -37
@@ -25,41 +25,41 @@
25
25
 
26
26
  /**
27
27
  * Communicates with a client by reading and writing from a socket.
28
+ * @param {Dispatcher} dispatcher The instance to send all parsed requests to.
28
29
  * @param {nsISocketTransport} transport The connected socket transport.
29
30
  * @constructor
30
31
  * @extends {nsIStreamListener}
31
32
  */
32
- function SocketListener(transport) {
33
- this.outstream = transport.
34
- openOutputStream(Components.interfaces.nsITransport.OPEN_BLOCKING, 0, 0);
33
+ function SocketListener(dispatcher, transport) {
35
34
 
36
- this.stream = transport.openInputStream(0, 0, 0);
37
- var cin = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
38
- createInstance(Components.interfaces.nsIConverterInputStream);
39
- cin.init(this.stream, SocketListener.CHARSET, 0, 0x0000);
40
-
41
- this.inputStream = cin;
42
-
43
- var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
44
- createInstance(Components.interfaces.nsIInputStreamPump);
45
- pump.init(this.stream, -1, -1, 0, 0, false);
46
- pump.asyncRead(this, null);
35
+ /**
36
+ * The instance to send all parsed requests to.
37
+ * @type {Dispatcher}
38
+ * @private
39
+ */
40
+ this.dispatcher_ = dispatcher;
47
41
 
48
- this.linesLeft = "";
49
- this.data = "";
50
- this.command = "";
51
- this.step = 0;
52
- this.readLength = false;
42
+ /**
43
+ * Transport for the socket this instance will read/write to.
44
+ * @type {nsISocketTransport}
45
+ * @private
46
+ */
47
+ this.transport_ = transport;
53
48
 
54
49
  /**
55
- * A reference to the command processor service. We grab the reference here
56
- * instead of on the prototype since the component may not be loaded yet.
57
- * @type {nsICommandProcessor}
50
+ * Output stream for the socket transport.
51
+ * @type {nsIOutputStream}
58
52
  * @private
59
53
  */
60
- this.commandProcessor_ = Components.
61
- classes['@googlecode.com/webdriver/command-processor;1'].
62
- getService(Components.interfaces.nsICommandProcessor);
54
+ this.outputStream_ = transport.openOutputStream(
55
+ Components.interfaces.nsITransport.OPEN_BLOCKING,
56
+ /*segmentSize=*/0,
57
+ /*segmentCount=*/0);
58
+
59
+ var socketInputStream = transport.openInputStream(
60
+ /*flags=*/0,
61
+ /*segmentSize=*/0,
62
+ /*segmentCount=*/0).QueryInterface(Components.interfaces.nsIAsyncInputStream);
63
63
 
64
64
  /**
65
65
  * The converter used when writing data back to the socket.
@@ -71,115 +71,365 @@ function SocketListener(transport) {
71
71
  createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
72
72
 
73
73
  this.converter_.charset = SocketListener.CHARSET;
74
+
75
+ /**
76
+ * The HTTP request method.
77
+ * @type {?Request.Method}
78
+ * @private
79
+ */
80
+ this.method_ = null;
81
+
82
+ /**
83
+ * The HTTP request headers as a JSON object.
84
+ * @type {object}
85
+ * @private
86
+ */
87
+ this.headers_ = {};
88
+ this.requestUrl_ = '';
89
+ this.body_ = '';
90
+ this.contentLengthRemaining_ = 0;
91
+
92
+ /**
93
+ * The raw UTF-8 request data that has been read so far.
94
+ * @type {string}
95
+ * @private
96
+ */
97
+ this.rawData_ = '';
98
+
99
+ var threadManager = Components.classes['@mozilla.org/thread-manager;1'].
100
+ getService(Components.interfaces.nsIThreadManager);
101
+
102
+ /**
103
+ * A reference to the main thread.
104
+ * @type {nsIThread}
105
+ * @private
106
+ * @const
107
+ */
108
+ this.mainThread_ = threadManager.mainThread;
109
+
110
+ socketInputStream.asyncWait(this, 0, 0, this.mainThread_);
74
111
  }
75
112
 
76
113
 
114
+ /**
115
+ * Enumeration of states for a SocketListener.
116
+ * @enum {number}
117
+ */
118
+ SocketListener.State = {
119
+ READING_REQUEST_LINE: 0,
120
+ READING_HEADERS: 1,
121
+ READING_BODY: 2,
122
+ FINISHED: 3
123
+ };
124
+
125
+
77
126
  /**
78
127
  * Charset used for socket I/O.
79
128
  * @type {string}
129
+ * @const
80
130
  */
81
131
  SocketListener.CHARSET = 'UTF-8';
82
132
 
83
133
 
84
134
  /**
85
- * Signals the start of a request. Each request lasts for the life of the
86
- * underlying socket connection and represents a session with a FirefoxDriver
87
- * client.
88
- * @see {nsIRequestObserver#onStartRequest}
135
+ * HTTP message sent in response to requests with an Expect:100-continue header.
136
+ * @type {string}
137
+ * @const
138
+ */
139
+ SocketListener.CONTINUE_MESSAGE = 'HTTP/1.1 100 Continue\r\n\r\n';
140
+
141
+
142
+ /**
143
+ * This instance's current state.
144
+ * @type {SocketListener.State}
145
+ * @private
146
+ */
147
+ SocketListener.prototype.state_ = SocketListener.State.READING_REQUEST_LINE;
148
+
149
+
150
+ /**
151
+ * Whether this instance has sent a 100-continue resposne to the client.
152
+ * @type {boolean}
153
+ * @private
154
+ */
155
+ SocketListener.prototype.continueSent_ = false;
156
+
157
+
158
+ /**
159
+ * Called when the underlying input stream has additional data ready to be read.
160
+ * @param {nsIAsyncInputStream} inputStream The stream with data ready.
161
+ * @see {nsIInputStreamCallback#onInputStreamReady}
162
+ */
163
+ SocketListener.prototype.onInputStreamReady = function(inputStream) {
164
+ var binaryInputStream = Components.
165
+ classes['@mozilla.org/binaryinputstream;1'].
166
+ createInstance(Components.interfaces.nsIBinaryInputStream);
167
+ binaryInputStream.setInputStream(inputStream);
168
+
169
+ // Utils.dumpn('onInputStreamReady');
170
+ var available;
171
+ try {
172
+ available = inputStream.available();
173
+ } catch (ex) {
174
+ // TODO: check for ex.result == Components.results.NS_BASE_STREAM_CLOSED?
175
+ this.transport_.close(0);
176
+ return;
177
+ }
178
+
179
+ var byteArray = binaryInputStream.readByteArray(available);
180
+ var converted = this.converter_.convertFromByteArray(byteArray, available);
181
+ this.rawData_ += converted;
182
+
183
+ // Utils.dumpn('...converted:\n' + converted.replace(/\r\n/g, '\\r\\n\n'));
184
+ // Utils.dumpn('...raw data:\n' + this.rawData_);
185
+
186
+ try {
187
+ switch (this.state_) {
188
+ case SocketListener.State.READING_REQUEST_LINE:
189
+ this.readRequestLine_();
190
+ if (this.state_ != SocketListener.State.READING_HEADERS) {
191
+ break;
192
+ }
193
+ // Read the request line, fall through to start reading headers.
194
+
195
+ case SocketListener.State.READING_HEADERS:
196
+ this.readHeaders_();
197
+ if (this.state_ != SocketListener.State.READING_BODY) {
198
+ break;
199
+ }
200
+ // Read all of the headers, fall through to start reading the body.
201
+
202
+ case SocketListener.State.READING_BODY:
203
+ this.readBody_();
204
+ break;
205
+ }
206
+
207
+ if (this.state_ != SocketListener.State.FINISHED) {
208
+ // Utils.dumpn('Waiting for more data...');
209
+ inputStream.asyncWait(this, 0, 0, this.mainThread_);
210
+ } else {
211
+ inputStream.close();
212
+ var clientRequest = new Request(
213
+ this.method_, this.requestUrl_, this.headers_, this.rawData_);
214
+ var clientResponse = new Response(
215
+ clientRequest, this.outputStream_);
216
+ // Utils.dumpn('Dispatching request:\n' + clientRequest.toDebugString());
217
+ this.dispatcher_.dispatch(clientRequest, clientResponse);
218
+ }
219
+ } catch (ex) {
220
+ // Utils.dumpn('Sending error:\n\t' + ex.toString());
221
+ var status = ex.isBadRequest ? ex.status : Response.INTERNAL_ERROR;
222
+ var response = new Response(null, this.outputStream_);
223
+ response.sendError(status, ex.toString(), 'text/plain');
224
+ this.transport_.close(0);
225
+ }
226
+ };
227
+
228
+
229
+ /**
230
+ * Reads a CRLF terminated line from the data that has been read from the socket
231
+ * thus far.
232
+ * @return {?string} The read line, minus the terminating CRLF, or null if none
233
+ * are yet available.
89
234
  */
90
- SocketListener.prototype.onStartRequest = function(request, context) {
235
+ SocketListener.prototype.readLine_ = function() {
236
+ while (true) {
237
+ var crlf = this.rawData_.search('\r\n');
238
+ if (crlf == -1) {
239
+ return null; // Haven't read a full line yet.
240
+ }
241
+
242
+ // Strip out the line from the request data.
243
+ var line = this.rawData_.substring(0, crlf).
244
+ replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
245
+ this.rawData_ = this.rawData_.substring(crlf + '\r\n'.length);
246
+
247
+ return line;
248
+ }
91
249
  };
92
250
 
93
251
 
94
252
  /**
95
- * Signals the end of a request (e.g. the underlying socket connection was
96
- * closed).
97
- * @see {nsIRequestObserver#onStopRequest}
253
+ * Attempt to parse the HTTP request line with the data that has been read from
254
+ * the socket thus far.
255
+ * @private
98
256
  */
99
- SocketListener.prototype.onStopRequest = function(request, context, status) {
257
+ SocketListener.prototype.readRequestLine_ = function() {
258
+ // Utils.dumpn('Reading request line...');
259
+ // Read the first non-blank line in the request. We skip blank lines
260
+ // according to RFC 2616, section 4.1.
261
+ var line = this.readLine_();
262
+ while (line != null && line.length == 0) {
263
+ line = this.readLine();
264
+ }
265
+
266
+ if (null == line) {
267
+ return; // Don't have a line to parse yet.
268
+ }
269
+
270
+ var parts = line.split(/\s+/);
271
+ if (parts.length < 3) {
272
+ throw new SocketListener.BadRequest('Invalid request line.');
273
+ }
274
+
275
+ this.method_ = parts.shift().toUpperCase();
276
+ this.path_ = parts.shift();
277
+ this.protocol_ = parts.shift().toUpperCase();
278
+
279
+ // We only support HTTP/1.1 requests.
280
+ if (this.protocol_ != 'HTTP/1.1') {
281
+ throw new SocketListener.BadRequest(
282
+ 'Not an HTTP/1.1 request: <' + this.protocol_ + '>');
283
+ }
284
+
285
+ // Make sure we were given a valid HTTP method.
286
+ if (typeof Request.Method[this.method_] == 'undefined') {
287
+ throw new SocketListener.BadRequest(
288
+ 'Invalid HTTP method: <' + this.method_ + '>');
289
+ }
290
+ // Utils.dumpn('\t' + this.method_ + ' ' + this.path_ + ' ' + this.protocol_);
291
+ this.state_ = SocketListener.State.READING_HEADERS;
100
292
  };
101
293
 
102
294
 
103
295
  /**
104
- * Called whenever another chunk of data is ready to be read from the socket.
105
- * @param {nsIRequest} request The data's origin.
106
- * @param {nsISupports} context User defined context.
107
- * @param {nsIInputStream} inputStream The input stream containing the data
108
- * chunk.
109
- * @param {number} offset The total number of bytes read by previous calls to
110
- * {@code #onDataAvailable}.
111
- * @param {number} count The number of bytes available in the stream.
112
- * @see {nsIStreamListener#onDataAvailable}
296
+ * Read headers from the data that has been read from the socket.
297
+ * @private
113
298
  */
114
- SocketListener.prototype.onDataAvailable = function(request, context,
115
- inputStream, offset,
116
- count) {
117
- var incoming = {};
118
- var read = this.inputStream.readString(count, incoming);
119
-
120
- var lines = incoming.value.split('\n');
121
- for (var j = 0; j < lines.length; j++) {
122
- if (0 == this.step) {
123
- var head = lines[j].split(": ", 2);
124
- if (head[0] == "Content-Length") {
125
- this.linesLeft = Number(head[1]);
126
- this.readLength = true;
127
- } else if (lines[j].length == 0 && this.readLength) {
128
- this.step++;
299
+ SocketListener.prototype.readHeaders_ = function() {
300
+ // Utils.dumpn('Reading headers...');
301
+ while (true) {
302
+ var line = this.readLine_(false);
303
+ if (null == line) {
304
+ return; // No headers available yet.
305
+ }
306
+
307
+ if (!line.length) {
308
+ // Blank line, end of headers.
309
+
310
+ // Make sure the host was specified.
311
+ if (typeof this.headers_['host'] == 'undefined') {
312
+ throw new SocketListener.BadRequest('No "Host" header specified');
129
313
  }
130
- } else {
131
- this.data += lines[j];
132
- this.linesLeft -= read;
133
314
 
134
- if (this.linesLeft <= 0) {
135
- this.executeCommand_();
136
- j++; // Consume the empty line
315
+ // Check the content-length.
316
+ if (typeof this.headers_['content-length'] == 'undefined') {
317
+ if (this.method_ == Request.Method.POST ||
318
+ this.method_ == Request.Method.PUT) {
319
+ throw new SocketListener.BadRequest(
320
+ 'No "Content-Length" header specified for POST or PUT request',
321
+ '411 Length Required');
322
+ }
323
+ } else {
324
+ var contentLength = parseInt(this.headers_['content-length']);
325
+ if (isNaN(contentLength)) {
326
+ throw new SocketListener.BadRequest(
327
+ 'Content-Length header is not a number: ' +
328
+ this.headers_['content-length']);
329
+ }
330
+ this.headers_['content-length'] = contentLength;
137
331
  }
332
+
333
+ // Reconstitute the original request URL.
334
+ this.requestUrl_ = 'http://' + this.headers_['host'] + this.path_;
335
+ try {
336
+ this.requestUrl_ = Components.
337
+ classes["@mozilla.org/network/io-service;1"].
338
+ getService(Components.interfaces.nsIIOService).
339
+ newURI(this.requestUrl_, null, null).
340
+ QueryInterface(Components.interfaces.nsIURL);
341
+ } catch (ex) {
342
+ throw new SocketListener.BadRequest(
343
+ 'Error parsing request URL: ' + this.requestUrl_);
344
+ }
345
+
346
+ this.state_ = SocketListener.State.READING_BODY;
347
+ return;
138
348
  }
139
- }
140
349
 
141
- if (this.linesLeft <= 0 && this.data) {
142
- this.executeCommand_();
350
+ var parts = line.match(/([^:\s]*)\s*:\s*([^\s].*)/);
351
+ if (!parts) {
352
+ throw new SocketListener.BadRequest(
353
+ 'Error parsing header field <' + line + '>');
354
+ }
355
+ this.headers_[parts[1].toLowerCase()] = parts[2];
356
+ // Utils.dumpn('\t' + parts[1] + ':' + parts[2]);
143
357
  }
144
358
  };
145
359
 
146
360
 
147
361
  /**
148
- * Parses the command data read from the socket into a JSON object and
149
- * dispatches it to the command processesor.
362
+ * Read the request body from the data that has been read thus far.
150
363
  * @private
151
364
  */
152
- SocketListener.prototype.executeCommand_ = function() {
153
- var self = this;
154
- var command = this.data;
155
- var callback = function(response) {
156
- //Utils.dumpn('writing to socket:\n' + response);
157
- var data = self.converter_.convertToByteArray(response, {});
158
- var header = "Length: " + data.length + "\n\n";
159
- self.outstream.write(header, header.length);
160
- self.outstream.flush();
161
-
162
- var stream = self.converter_.convertToInputStream(response);
163
- self.outstream.writeFrom(stream, data.length);
164
- self.outstream.flush();
165
- stream.close();
166
- };
167
-
168
- // Clear data for the next read.
169
- this.data = '';
170
- this.linesLeft = 0;
171
- this.step = 0;
172
- this.readLength = 0;
365
+ SocketListener.prototype.readBody_ = function() {
366
+ // Utils.dumpn('Reading body...');
367
+ if (!this.continueSent_) {
368
+ // Need to send a 100 Continue if it is expected.
369
+ // For more info, see RFC 2616, section 8.2.3
370
+ if (this.headers_['expect'] == '100-continue') {
371
+ // Utils.dumpn('Sending 100-continue...');
372
+ this.continueSent_ = true;
373
+ var continueResp = new Response(null, this.outputStream_);
374
+ continueResp.setStatus(Response.CONTINUE);
375
+ continueResp.commit();
376
+ }
377
+ }
173
378
 
174
- try {
175
- this.commandProcessor_.execute(command, callback);
176
- } catch (e) {
177
- Utils.dump(e);
178
- Utils.dumpn(command);
179
-
180
- // Something has gone seriously wrong. Quit the browser.
181
- this.commandProcessor_.execute(
182
- JSON.stringify({'commandName': 'quit'}),
183
- function() {});
379
+ if (this.method_ != Request.Method.POST &&
380
+ this.method_ != Request.Method.PUT) {
381
+ // No body to read. Technically, if the client sends a body (as indicated
382
+ // by the Content-Length header, we should read it in, even if it's ignored
383
+ // for the request method. But this can lead to indefinite blocking with
384
+ // clients that specify a Content-Length on a GET request and then never
385
+ // send the body.
386
+ this.state_ = SocketListener.State.FINISHED;
387
+ return;
388
+ }
389
+
390
+ // This rigmarole is needed so we know the number of bytes read so we can tell
391
+ // if we've read the entire body.
392
+ var escaped = encodeURIComponent(this.rawData_);
393
+ var escapedCharCount = 0;
394
+ if (escaped.indexOf('%', 0) != -1) {
395
+ escapedCharCount = escaped.split('%').length - 1;
396
+ }
397
+ var bytesRead = escaped.length - (2 * escapedCharCount);
398
+ var bytesRemaining = this.headers_['content-length'] - bytesRead;
399
+
400
+ // If we read more data than the Content-Length header specified, then too
401
+ // much data was sent by the client, and we consider this a malformed
402
+ // request.
403
+ if (bytesRemaining < 0) {
404
+ throw new SocketListener.BadRequest(
405
+ 'Request body is longer than indicated "Content-Length" header;' +
406
+ ' expected <' + this.headers_['content-length'] +'>, ' +
407
+ ' but was <' + bytesRead + '>');
408
+ } else if (bytesRemaining == 0) {
409
+ // Utils.dumpn('Finished reading body!');
410
+ this.state_ = SocketListener.State.FINISHED;
411
+ // } else {
412
+ // Utils.dumpn(
413
+ // 'Read ' + bytesRead + '; ' + bytesRemaining + ' bytes remaining');
184
414
  }
185
415
  };
416
+
417
+
418
+ /**
419
+ * Thrown when a bad request is parsed.
420
+ * @param {string} message The error message to return to the client.
421
+ * @param {number} opt_status The HTTP status code to use when returning the
422
+ * error to the client. Defaults to 400.
423
+ * @constructor
424
+ */
425
+ SocketListener.BadRequest = function(message, opt_status) {
426
+ this.message = message;
427
+ this.status = opt_status || Response.BAD_REQUEST;
428
+ this.isBadRequest = true;
429
+ };
430
+
431
+
432
+ /** @override */
433
+ SocketListener.BadRequest.prototype.toString = function() {
434
+ return this.message;
435
+ };