-
-
Notifications
You must be signed in to change notification settings - Fork 35k
Process 100, 102-199 status codes according to specs. #18033
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
5200fab
fba79a8
58bb0ca
79301fa
b2c81fc
fd33e98
2113f0a
65405c4
ea72928
29cae69
5f4049c
d7bacbb
12e0566
e51c7ae
0386a11
e46b997
130e729
89e4552
1eee678
1c2e86d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -400,6 +400,37 @@ Emitted when the server sends a '100 Continue' HTTP response, usually because | |
| the request contained 'Expect: 100-continue'. This is an instruction that | ||
| the client should send the request body. | ||
|
|
||
| ### Event: 'information' | ||
| <!-- YAML | ||
| added: REPLACEME | ||
| --> | ||
|
|
||
| Emitted when the server sends a 1xx response (excluding 101 Upgrade). This | ||
| event is emitted with a callback containing an object with a status code. | ||
|
|
||
| ```js | ||
| const http = require('http'); | ||
|
|
||
| const options = { | ||
| hostname: '127.0.0.1', | ||
| port: 8080, | ||
| path: '/length_request' | ||
| }; | ||
|
|
||
| // make a request | ||
| const req = http.request(options); | ||
| req.end(); | ||
|
|
||
| req.on('information', (res) => { | ||
| console.log('got information prior to main response: ' + res.statusCode); | ||
| }); | ||
| ``` | ||
|
|
||
| 101 Upgrade statuses do not fire this event due to their break from the | ||
| traditional HTTP request/response chain, such as web sockets, in-place TLS | ||
| upgrades, or HTTP 2.0. To be notified of 101 Upgrade notices, listen for the | ||
| [`'upgrade'`]() event instead. | ||
|
|
||
|
||
| ### Event: 'response' | ||
| <!-- YAML | ||
| added: v0.1.0 | ||
|
|
@@ -1382,6 +1413,14 @@ which has been transmitted are equal or not. | |
| Attempting to set a header field name or value that contains invalid characters | ||
| will result in a [`TypeError`][] being thrown. | ||
|
|
||
| ### response.writeProcessing() | ||
| <!-- YAML | ||
| added: REPLACEME | ||
| --> | ||
|
|
||
| Sends a HTTP/1.1 102 Processing message to the client, indicating that | ||
| the request body should be sent. | ||
|
|
||
| ## Class: http.IncomingMessage | ||
| <!-- YAML | ||
| added: v0.1.17 | ||
|
|
@@ -1881,6 +1920,7 @@ const req = http.request(options, (res) => { | |
| [`'checkContinue'`]: #http_event_checkcontinue | ||
| [`'request'`]: #http_event_request | ||
| [`'response'`]: #http_event_response | ||
| [`'upgrade'`]: #http_event_upgrade | ||
| [`Agent`]: #http_class_http_agent | ||
| [`Duplex`]: stream.html#stream_class_stream_duplex | ||
| [`EventEmitter`]: events.html#events_class_eventemitter | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -447,16 +447,26 @@ function socketOnData(d) { | |
| socket.destroy(); | ||
| } | ||
| } else if (parser.incoming && parser.incoming.complete && | ||
| // When the status code is 100 (Continue), the server will | ||
| // send a final response after this client sends a request | ||
| // body. So, we must not free the parser. | ||
| parser.incoming.statusCode !== 100) { | ||
| // When the status code is informational (100, 102-199), | ||
| // the server will send a final response after this client | ||
| // sends a request body, so we must not free the parser. | ||
| // 101 (Switching Protocols) and all other status codes | ||
| // should be processed normally. | ||
| !statusIsInformational(parser.incoming.statusCode)) { | ||
| socket.removeListener('data', socketOnData); | ||
| socket.removeListener('end', socketOnEnd); | ||
| freeParser(parser, req, socket); | ||
| } | ||
| } | ||
|
|
||
| // client | ||
|
||
| function statusIsInformational(status) { | ||
| // 100 (Continue) RFC7231 Section 6.2.1 | ||
| // 102 (Processing) RFC2518 | ||
| // 103 (Early Hints) RFC8297 | ||
| // 104-199 (Unassigned) | ||
| return (status < 200 && status >= 100 && status !== 101); | ||
| } | ||
|
|
||
| // client | ||
| function parserOnIncomingClient(res, shouldKeepAlive) { | ||
|
|
@@ -486,10 +496,24 @@ function parserOnIncomingClient(res, shouldKeepAlive) { | |
| return 2; // Skip body and treat as Upgrade. | ||
| } | ||
|
|
||
| if (res.statusCode === 100) { | ||
| // restart the parser, as this is a continue message. | ||
| // Responses to HEAD requests are crazy. | ||
| // HEAD responses aren't allowed to have an entity-body | ||
| // but *can* have a content-length which actually corresponds | ||
| // to the content-length of the entity-body had the request | ||
| // been a GET. | ||
| var isHeadResponse = req.method === 'HEAD'; | ||
| debug('AGENT isHeadResponse', isHeadResponse); | ||
|
||
|
|
||
| if (statusIsInformational(res.statusCode)) { | ||
| // Restart the parser, as this is a 1xx informational message. | ||
| req.res = null; // Clear res so that we don't hit double-responses. | ||
| req.emit('continue'); | ||
| // Maintain compatibility by sending 100-specific events | ||
| if (res.statusCode === 100) { | ||
| req.emit('continue'); | ||
| } | ||
| // Send information events to all 1xx responses except 101 Upgrade. | ||
| req.emit('information', { statusCode: res.statusCode }); | ||
|
|
||
| return 1; // Skip body but don't treat as Upgrade. | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| 'use strict'; | ||
| require('../common'); | ||
| const assert = require('assert'); | ||
| const http = require('http'); | ||
|
|
||
| const test_res_body = 'other stuff!\n'; | ||
| let processing_count = 0; | ||
|
|
||
| const server = http.createServer((req, res) => { | ||
| console.error('Server sending informational message #1...'); | ||
| res.writeProcessing(); | ||
| console.error('Server sending informational message #2...'); | ||
| res.writeProcessing(); | ||
| console.error('Server sending full response...'); | ||
| res.writeHead(200, { | ||
| 'Content-Type': 'text/plain', | ||
| 'ABCD': '1' | ||
| }); | ||
| res.end(test_res_body); | ||
| }); | ||
|
|
||
| server.listen(0, function() { | ||
| const req = http.request({ | ||
| port: this.address().port, | ||
| path: '/world' | ||
| }); | ||
| req.end(); | ||
| console.error('Client sending request...'); | ||
|
|
||
| let body = ''; | ||
|
|
||
| req.on('information', function(res) { | ||
| console.error('Client got 102 Processing...'); | ||
| processing_count++; | ||
| }); | ||
|
|
||
| req.on('response', function(res) { | ||
| assert.strictEqual(processing_count, 2, | ||
|
||
| 'Full response received before all 102 Processing'); | ||
| assert.strictEqual(200, res.statusCode, | ||
| `Final status code was ${res.statusCode}, not 200.`); | ||
| res.setEncoding('utf8'); | ||
| res.on('data', function(chunk) { body += chunk; }); | ||
| res.on('end', function() { | ||
| console.error('Got full response.'); | ||
| assert.strictEqual(body, test_res_body, 'Response body doesn\'t match.'); | ||
|
||
| assert.ok('abcd' in res.headers, 'Response headers missing.'); | ||
|
||
| server.close(); | ||
| }); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking nit: Using capital letters as first character would be great :-) And maybe also a dot at the end.