fix: pass full response headers in net module (#21552)
This commit is contained in:
parent
2d0bf81bd4
commit
ceacadb4f4
3 changed files with 63 additions and 13 deletions
|
@ -53,18 +53,27 @@ class IncomingMessage extends Readable {
|
||||||
|
|
||||||
get headers () {
|
get headers () {
|
||||||
const filteredHeaders = {}
|
const filteredHeaders = {}
|
||||||
const rawHeaders = this._responseHead.headers
|
const { rawHeaders } = this._responseHead
|
||||||
Object.keys(rawHeaders).forEach(header => {
|
rawHeaders.forEach(header => {
|
||||||
if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) {
|
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
|
||||||
|
discardableDuplicateHeaders.has(header.key)) {
|
||||||
// do nothing with discardable duplicate headers
|
// do nothing with discardable duplicate headers
|
||||||
} else {
|
} else {
|
||||||
if (header === 'set-cookie') {
|
if (header.key === 'set-cookie') {
|
||||||
// keep set-cookie as an array per Node.js rules
|
// keep set-cookie as an array per Node.js rules
|
||||||
// see https://nodejs.org/api/http.html#http_message_headers
|
// see https://nodejs.org/api/http.html#http_message_headers
|
||||||
filteredHeaders[header] = rawHeaders[header]
|
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||||
|
filteredHeaders[header.key].push(header.value)
|
||||||
|
} else {
|
||||||
|
filteredHeaders[header.key] = [header.value]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// for non-cookie headers, the values are joined together with ', '
|
// for non-cookie headers, the values are joined together with ', '
|
||||||
filteredHeaders[header] = rawHeaders[header].join(', ')
|
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
|
||||||
|
filteredHeaders[header.key] += `, ${header.value}`
|
||||||
|
} else {
|
||||||
|
filteredHeaders[header.key] = header.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,6 +29,28 @@
|
||||||
#include "shell/common/gin_helper/object_template_builder.h"
|
#include "shell/common/gin_helper/object_template_builder.h"
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
|
|
||||||
|
namespace gin {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<network::mojom::HttpRawHeaderPairPtr> {
|
||||||
|
static v8::Local<v8::Value> ToV8(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
const network::mojom::HttpRawHeaderPairPtr& pair) {
|
||||||
|
gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(isolate);
|
||||||
|
dict.Set("key", base::ToLowerASCII(pair->key));
|
||||||
|
dict.Set("value", pair->value);
|
||||||
|
return dict.GetHandle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gin
|
||||||
|
|
||||||
|
namespace electron {
|
||||||
|
|
||||||
|
namespace api {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
class BufferDataSource : public mojo::DataPipeProducer::DataSource {
|
class BufferDataSource : public mojo::DataPipeProducer::DataSource {
|
||||||
public:
|
public:
|
||||||
explicit BufferDataSource(base::span<char> buffer) {
|
explicit BufferDataSource(base::span<char> buffer) {
|
||||||
|
@ -198,12 +220,6 @@ class JSChunkedDataPipeGetter : public gin::Wrappable<JSChunkedDataPipeGetter>,
|
||||||
gin::WrapperInfo JSChunkedDataPipeGetter::kWrapperInfo = {
|
gin::WrapperInfo JSChunkedDataPipeGetter::kWrapperInfo = {
|
||||||
gin::kEmbedderNativeGin};
|
gin::kEmbedderNativeGin};
|
||||||
|
|
||||||
namespace electron {
|
|
||||||
|
|
||||||
namespace api {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
|
const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
|
||||||
net::DefineNetworkTrafficAnnotation("electron_net_module", R"(
|
net::DefineNetworkTrafficAnnotation("electron_net_module", R"(
|
||||||
semantics {
|
semantics {
|
||||||
|
@ -341,6 +357,10 @@ gin_helper::WrappableBase* SimpleURLLoaderWrapper::New(gin::Arguments* args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chromium filters headers using browser rules, while for net module we have
|
||||||
|
// every header passed.
|
||||||
|
request->report_raw_headers = true;
|
||||||
|
|
||||||
v8::Local<v8::Value> body;
|
v8::Local<v8::Value> body;
|
||||||
v8::Local<v8::Value> chunk_pipe_getter;
|
v8::Local<v8::Value> chunk_pipe_getter;
|
||||||
if (opts.Get("body", &body)) {
|
if (opts.Get("body", &body)) {
|
||||||
|
@ -415,8 +435,12 @@ void SimpleURLLoaderWrapper::OnResponseStarted(
|
||||||
gin::Dictionary dict = gin::Dictionary::CreateEmpty(isolate());
|
gin::Dictionary dict = gin::Dictionary::CreateEmpty(isolate());
|
||||||
dict.Set("statusCode", response_head.headers->response_code());
|
dict.Set("statusCode", response_head.headers->response_code());
|
||||||
dict.Set("statusMessage", response_head.headers->GetStatusText());
|
dict.Set("statusMessage", response_head.headers->GetStatusText());
|
||||||
dict.Set("headers", response_head.headers.get());
|
|
||||||
dict.Set("httpVersion", response_head.headers->GetHttpVersion());
|
dict.Set("httpVersion", response_head.headers->GetHttpVersion());
|
||||||
|
// Note that |response_head.headers| are filtered by Chromium and should not
|
||||||
|
// be used here.
|
||||||
|
DCHECK(response_head.raw_request_response_info);
|
||||||
|
dict.Set("rawHeaders",
|
||||||
|
response_head.raw_request_response_info->response_headers);
|
||||||
Emit("response-started", final_url, dict);
|
Emit("response-started", final_url, dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -616,6 +616,23 @@ describe('net module', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should be able to receive cookies', (done) => {
|
||||||
|
const cookie = ['cookie1', 'cookie2']
|
||||||
|
respondOnce.toSingleURL((request, response) => {
|
||||||
|
response.statusCode = 200
|
||||||
|
response.statusMessage = 'OK'
|
||||||
|
response.setHeader('set-cookie', cookie)
|
||||||
|
response.end()
|
||||||
|
}).then(serverUrl => {
|
||||||
|
const urlRequest = net.request(serverUrl)
|
||||||
|
urlRequest.on('response', (response) => {
|
||||||
|
expect(response.headers['set-cookie']).to.have.same.members(cookie)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
urlRequest.end()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('should be able to abort an HTTP request before first write', async () => {
|
it('should be able to abort an HTTP request before first write', async () => {
|
||||||
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
response.end()
|
response.end()
|
||||||
|
|
Loading…
Reference in a new issue