Skip to content

Commit 1b08867

Browse files
authored
Merge pull request #562 from ipfs-shipyard/fix/files.add-in-chrome
Workaround for 'invalid Read on closed Body' in files.add (webext in Chrome) > Chrome does allow setting `Expect` via [`onBeforeSendHeaders`](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/onBeforeSendHeaders) WebExtension API This means we now have a workaround for #480 that works in both Firefox and Chromium. Details: #480 (comment) & ipfs/kubo#5168 (comment) Closes #480
2 parents 4f32fb7 + ce96cb3 commit 1b08867

File tree

2 files changed

+91
-3
lines changed

2 files changed

+91
-3
lines changed

add-on/src/lib/ipfs-request.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,43 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
8282
// There is a bug in go-ipfs related to keep-alive connections
8383
// that results in partial response for ipfs.files.add
8484
// mangled by error "http: invalid Read on closed Body"
85-
// More info: https://github.com/ipfs/go-ipfs/issues/5168
86-
if (request.url.includes('/api/v0/add')) {
85+
// More info (ipfs-companion): https://github.com/ipfs-shipyard/ipfs-companion/issues/480
86+
// More info (go-ipfs): https://github.com/ipfs/go-ipfs/issues/5168
87+
if (request.url.includes('/api/v0/add') && request.url.includes('stream-channels=true')) {
88+
let addExpectHeader = true
89+
const expectHeader = { name: 'Expect', value: '100-continue' }
90+
const warningMsg = '[ipfs-companion] Executing "Expect: 100-continue" workaround for ipfs.files.add due to https://github.com/ipfs/go-ipfs/issues/5168'
8791
for (let header of request.requestHeaders) {
88-
if (header.name === 'Connection') {
92+
// Workaround A: https://github.com/ipfs/go-ipfs/issues/5168#issuecomment-401417420
93+
// (works in Firefox, but Chromium does not expose Connection header)
94+
/* (disabled so we use the workaround B in all browsers)
95+
if (header.name === 'Connection' && header.value !== 'close') {
8996
console.warn('[ipfs-companion] Executing "Connection: close" workaround for ipfs.files.add due to https://github.com/ipfs/go-ipfs/issues/5168')
9097
header.value = 'close'
98+
addExpectHeader = false
9199
break
92100
}
101+
*/
102+
// Workaround B: https://github.com/ipfs-shipyard/ipfs-companion/issues/480#issuecomment-417657758
103+
// (works in Firefox 63 AND Chromium 67)
104+
if (header.name === expectHeader.name) {
105+
addExpectHeader = false
106+
if (header.value !== expectHeader.value) {
107+
console.log(warningMsg)
108+
header.value = expectHeader.value
109+
}
110+
break
111+
}
112+
}
113+
if (addExpectHeader) {
114+
console.log(warningMsg)
115+
request.requestHeaders.push(expectHeader)
93116
}
94117
}
95118
// For some reason js-ipfs-api sent requests with "Origin: null" under Chrome
96119
// which produced '403 - Forbidden' error.
97120
// This workaround removes bogus header from API requests
121+
// TODO: check if still necessary
98122
for (let i = 0; i < request.requestHeaders.length; i++) {
99123
let header = request.requestHeaders[i]
100124
if (header.name === 'Origin' && (header.value == null || header.value === 'null')) {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use strict'
2+
const { describe, it, before, beforeEach, after } = require('mocha')
3+
const { expect } = require('chai')
4+
const { URL } = require('url')
5+
const browser = require('sinon-chrome')
6+
const { initState } = require('../../../add-on/src/lib/state')
7+
const createRuntimeChecks = require('../../../add-on/src/lib/runtime-checks')
8+
const { createRequestModifier } = require('../../../add-on/src/lib/ipfs-request')
9+
const createDnslinkResolver = require('../../../add-on/src/lib/dnslink')
10+
const { createIpfsPathValidator } = require('../../../add-on/src/lib/ipfs-path')
11+
const { optionDefaults } = require('../../../add-on/src/lib/options')
12+
13+
// const nodeTypes = ['external', 'embedded']
14+
15+
describe('modifyRequest processing', function () {
16+
let state, dnslinkResolver, ipfsPathValidator, modifyRequest, runtime
17+
18+
before(function () {
19+
global.URL = URL
20+
global.browser = browser
21+
})
22+
23+
beforeEach(async function () {
24+
state = initState(optionDefaults)
25+
const getState = () => state
26+
dnslinkResolver = createDnslinkResolver(getState)
27+
runtime = Object.assign({}, await createRuntimeChecks(browser)) // make it mutable for tests
28+
ipfsPathValidator = createIpfsPathValidator(getState, dnslinkResolver)
29+
modifyRequest = createRequestModifier(getState, dnslinkResolver, ipfsPathValidator, runtime)
30+
})
31+
32+
describe('a request to <apiURL>/api/v0/add with stream-channels=true', function () {
33+
const expectHeader = { name: 'Expect', value: '100-continue' }
34+
it('should apply the "Expect: 100-continue" fix for https://github.com/ipfs/go-ipfs/issues/5168 ', function () {
35+
const request = {
36+
method: 'POST',
37+
requestHeaders: [
38+
{ name: 'Content-Type', value: 'multipart/form-data; boundary=--------------------------015695779813832455524979' }
39+
],
40+
type: 'xmlhttprequest',
41+
url: `${state.apiURLString}api/v0/add?progress=true&wrapWithDirectory=true&pin=true&wrap-with-directory=true&stream-channels=true`
42+
}
43+
expect(modifyRequest.onBeforeSendHeaders(request).requestHeaders).to.deep.include(expectHeader)
44+
})
45+
})
46+
47+
describe('a request to <apiURL>/api/v0/ with Origin=null', function () {
48+
it('should remove Origin header ', function () {
49+
const bogusOriginHeader = { name: 'Origin', value: 'null' }
50+
const request = {
51+
requestHeaders: [ bogusOriginHeader ],
52+
type: 'xmlhttprequest',
53+
url: `${state.apiURLString}api/v0/id`
54+
}
55+
expect(modifyRequest.onBeforeSendHeaders(request).requestHeaders).to.not.include(bogusOriginHeader)
56+
})
57+
})
58+
59+
after(function () {
60+
delete global.URL
61+
delete global.browser
62+
browser.flush()
63+
})
64+
})

0 commit comments

Comments
 (0)