fix: allow reading body from non-2xx responses in net.request (#21055)
* fix(urlrequest): allow non-2xx repsponse results - closes #21046 * test(net): add test cases to verify non-2xx body * test(session): update spec to match clientrequest behavior * test(net): update test cases to match clientrequest behavior * spec: clean up async net spec
This commit is contained in:
parent
2e25999c52
commit
ca61d2fae7
3 changed files with 70 additions and 9 deletions
|
@ -286,6 +286,8 @@ bool URLRequest::Write(v8::Local<v8::Value> data, bool is_last) {
|
||||||
network::ResourceRequest* request_ref = request_.get();
|
network::ResourceRequest* request_ref = request_.get();
|
||||||
loader_ = network::SimpleURLLoader::Create(std::move(request_),
|
loader_ = network::SimpleURLLoader::Create(std::move(request_),
|
||||||
kTrafficAnnotation);
|
kTrafficAnnotation);
|
||||||
|
|
||||||
|
loader_->SetAllowHttpErrorResults(true);
|
||||||
loader_->SetOnResponseStartedCallback(
|
loader_->SetOnResponseStartedCallback(
|
||||||
base::Bind(&URLRequest::OnResponseStarted, weak_factory_.GetWeakPtr()));
|
base::Bind(&URLRequest::OnResponseStarted, weak_factory_.GetWeakPtr()));
|
||||||
loader_->SetOnRedirectCallback(
|
loader_->SetOnRedirectCallback(
|
||||||
|
|
|
@ -219,26 +219,31 @@ describe('net module', () => {
|
||||||
expect(loginAuthInfo.scheme).to.equal('basic')
|
expect(loginAuthInfo.scheme).to.equal('basic')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should produce an error on the response object when cancelling authentication', async () => {
|
it('should response when cancelling authentication', async () => {
|
||||||
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
if (!request.headers.authorization) {
|
if (!request.headers.authorization) {
|
||||||
return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end()
|
response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' })
|
||||||
|
response.end('unauthenticated')
|
||||||
|
} else {
|
||||||
|
response.writeHead(200).end('ok')
|
||||||
}
|
}
|
||||||
response.writeHead(200).end('ok')
|
|
||||||
})
|
})
|
||||||
await expect(new Promise((resolve, reject) => {
|
expect(await new Promise((resolve, reject) => {
|
||||||
const request = net.request({ method: 'GET', url: serverUrl })
|
const request = net.request({ method: 'GET', url: serverUrl })
|
||||||
request.on('response', (response) => {
|
request.on('response', (response) => {
|
||||||
|
let data = ''
|
||||||
response.on('error', reject)
|
response.on('error', reject)
|
||||||
response.on('data', () => {})
|
response.on('data', (chunk) => {
|
||||||
response.on('end', () => resolve())
|
data += chunk
|
||||||
|
})
|
||||||
|
response.on('end', () => resolve(data))
|
||||||
})
|
})
|
||||||
request.on('login', (authInfo, cb) => {
|
request.on('login', (authInfo, cb) => {
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
request.on('error', reject)
|
request.on('error', reject)
|
||||||
request.end()
|
request.end()
|
||||||
})).to.eventually.be.rejectedWith('net::ERR_HTTP_RESPONSE_CODE_FAILURE')
|
})).to.equal('unauthenticated')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should share credentials with WebContents', async () => {
|
it('should share credentials with WebContents', async () => {
|
||||||
|
@ -717,6 +722,53 @@ describe('net module', () => {
|
||||||
expect(abortsEmitted).to.equal(1, 'request should emit exactly 1 "abort" event')
|
expect(abortsEmitted).to.equal(1, 'request should emit exactly 1 "abort" event')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should allow to read response body from non-2xx response', async () => {
|
||||||
|
const bodyData = randomString(kOneKiloByte)
|
||||||
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
response.statusCode = 404
|
||||||
|
response.end(bodyData)
|
||||||
|
})
|
||||||
|
|
||||||
|
let requestResponseEventEmitted = false
|
||||||
|
let responseDataEventEmitted = false
|
||||||
|
|
||||||
|
const urlRequest = net.request(serverUrl)
|
||||||
|
const eventHandlers = Promise.all([
|
||||||
|
emittedOnce(urlRequest, 'response')
|
||||||
|
.then(async (params: any[]) => {
|
||||||
|
const response: Electron.IncomingMessage = params[0]
|
||||||
|
requestResponseEventEmitted = true
|
||||||
|
const statusCode = response.statusCode
|
||||||
|
expect(statusCode).to.equal(404)
|
||||||
|
const buffers: Buffer[] = []
|
||||||
|
response.on('data', (chunk) => {
|
||||||
|
buffers.push(chunk)
|
||||||
|
responseDataEventEmitted = true
|
||||||
|
})
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
response.on('error', () => {
|
||||||
|
reject(new Error('error emitted'))
|
||||||
|
})
|
||||||
|
emittedOnce(response, 'end')
|
||||||
|
.then(() => {
|
||||||
|
const receivedBodyData = Buffer.concat(buffers)
|
||||||
|
expect(receivedBodyData.toString()).to.equal(bodyData)
|
||||||
|
})
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
emittedOnce(urlRequest, 'close')
|
||||||
|
])
|
||||||
|
|
||||||
|
urlRequest.end()
|
||||||
|
|
||||||
|
await eventHandlers
|
||||||
|
|
||||||
|
expect(requestResponseEventEmitted).to.equal(true)
|
||||||
|
expect(responseDataEventEmitted).to.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
describe('webRequest', () => {
|
describe('webRequest', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
session.defaultSession.webRequest.onBeforeRequest(null)
|
session.defaultSession.webRequest.onBeforeRequest(null)
|
||||||
|
|
|
@ -511,12 +511,19 @@ describe('session module', () => {
|
||||||
const fetch = (url: string) => new Promise((resolve, reject) => {
|
const fetch = (url: string) => new Promise((resolve, reject) => {
|
||||||
const request = net.request({ url, session: ses })
|
const request = net.request({ url, session: ses })
|
||||||
request.on('response', (response) => {
|
request.on('response', (response) => {
|
||||||
let data = ''
|
let data: string | null = null
|
||||||
response.on('data', (chunk) => {
|
response.on('data', (chunk) => {
|
||||||
|
if (!data) {
|
||||||
|
data = ''
|
||||||
|
}
|
||||||
data += chunk
|
data += chunk
|
||||||
})
|
})
|
||||||
response.on('end', () => {
|
response.on('end', () => {
|
||||||
resolve(data)
|
if (!data) {
|
||||||
|
reject(new Error('Empty response'))
|
||||||
|
} else {
|
||||||
|
resolve(data)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
response.on('error', (error: any) => { reject(new Error(error)) })
|
response.on('error', (error: any) => { reject(new Error(error)) })
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue