diff --git a/shell/browser/api/atom_api_url_request.cc b/shell/browser/api/atom_api_url_request.cc index 6c5b6d191b57..a50f6968bd1f 100644 --- a/shell/browser/api/atom_api_url_request.cc +++ b/shell/browser/api/atom_api_url_request.cc @@ -286,6 +286,8 @@ bool URLRequest::Write(v8::Local data, bool is_last) { network::ResourceRequest* request_ref = request_.get(); loader_ = network::SimpleURLLoader::Create(std::move(request_), kTrafficAnnotation); + + loader_->SetAllowHttpErrorResults(true); loader_->SetOnResponseStartedCallback( base::Bind(&URLRequest::OnResponseStarted, weak_factory_.GetWeakPtr())); loader_->SetOnRedirectCallback( diff --git a/spec-main/api-net-spec.ts b/spec-main/api-net-spec.ts index 664effcf2ad6..32ea06dc5906 100644 --- a/spec-main/api-net-spec.ts +++ b/spec-main/api-net-spec.ts @@ -219,26 +219,31 @@ describe('net module', () => { 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) => { 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 }) request.on('response', (response) => { + let data = '' response.on('error', reject) - response.on('data', () => {}) - response.on('end', () => resolve()) + response.on('data', (chunk) => { + data += chunk + }) + response.on('end', () => resolve(data)) }) request.on('login', (authInfo, cb) => { cb() }) request.on('error', reject) request.end() - })).to.eventually.be.rejectedWith('net::ERR_HTTP_RESPONSE_CODE_FAILURE') + })).to.equal('unauthenticated') }) 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') }) + 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', () => { afterEach(() => { session.defaultSession.webRequest.onBeforeRequest(null) diff --git a/spec-main/api-session-spec.ts b/spec-main/api-session-spec.ts index 899b158f80c0..9692def29a19 100644 --- a/spec-main/api-session-spec.ts +++ b/spec-main/api-session-spec.ts @@ -511,12 +511,19 @@ describe('session module', () => { const fetch = (url: string) => new Promise((resolve, reject) => { const request = net.request({ url, session: ses }) request.on('response', (response) => { - let data = '' + let data: string | null = null response.on('data', (chunk) => { + if (!data) { + data = '' + } data += chunk }) response.on('end', () => { - resolve(data) + if (!data) { + reject(new Error('Empty response')) + } else { + resolve(data) + } }) response.on('error', (error: any) => { reject(new Error(error)) }) })