Skip to content
This repository has been archived by the owner on Mar 10, 2020. It is now read-only.

feat: add support for custom headers to send-request #741

Merged
merged 6 commits into from
Jun 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ $ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials "[\"true\"
$ ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods "[\"PUT\", \"POST\", \"GET\"]"
```

### Custom Headers

If you wish to send custom headers with each request made by this library, for example, the Authorization header. You can use the config to do so:

```
const ipfs = IpfsApi({
host: 'localhost',
port: 5001,
protocol: 'http',
headers: {
authorization: 'Bearer ' + TOKEN
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with the http server lib we're using here -- does it transparently convert header keys to camel case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lgierth I've often wondered about casing of http headers, especially across languages:

https://stackoverflow.com/questions/5258977/are-http-headers-case-sensitive

Here are come examples of headers being set prior to this change.

https://github.com/ipfs/js-ipfs-api/blob/master/src/utils/send-request.js#L113

Its worth noting that user agent header will be overwritten by this code even if its provided in the config.

The headers object is passed directly to the request library:

https://github.com/ipfs/js-ipfs-api/blob/master/src/utils/send-request.js#L162

https://github.com/ipfs/js-ipfs-api/blob/master/src/utils/request.js

Here is an interesting blog post on header casing and some challenges it can cause:

https://medium.com/walmartlabs/hacking-node-core-http-module-f2a10ea3028e

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long story short, i don't think header casing should be altered by code in this library.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually a pretty hard and annoying problem, particularly in Node.js.

The basic rule of thumb is that you need to preserve casing on user inputs. This is very problematic if you need to check and manipulate headers elsewhere in code.

I wrote a library to deal with this in request, so it's pretty widely used. https://github.com/request/caseless, basically just wraps the headers object and provides an API for you to manipulate it in a caseless way while still maintaining the case of the user inputs.

}
})
```

## Usage

### API
Expand Down
2 changes: 1 addition & 1 deletion src/utils/send-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function requestAPI (config, options, callback) {
delete options.qs.followSymlinks

const method = 'POST'
const headers = {}
const headers = Object.assign({}, config.headers)

if (isNode) {
// Browsers do not allow you to modify the user agent
Expand Down
61 changes: 61 additions & 0 deletions test/custom-headers.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-env mocha */
'use strict'

const isNode = require('detect-node')
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)

const IPFSApi = require('../src')
const f = require('./utils/factory')

describe('custom headers', function () {
// do not test in browser
if (!isNode) { return }
this.timeout(50 * 1000) // slow CI
let ipfs
let ipfsd
// initialize ipfs with custom headers
before(done => {
f.spawn({ initOptions: { bits: 1024 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = IPFSApi({
host: 'localhost',
port: 6001,
protocol: 'http',
headers: {
authorization: 'Bearer ' + 'YOLO'
}
})
done()
})
})

it('are supported', done => {
// spin up a test http server to inspect the requests made by the library
const server = require('http').createServer((req, res) => {
req.on('data', () => {})
req.on('end', () => {
res.writeHead(200)
res.end()
// ensure custom headers are present
expect(req.headers.authorization).to.equal('Bearer ' + 'YOLO')
server.close()
done()
})
})

server.listen(6001, () => {
ipfs.id((err, res) => {
if (err) {
throw new Error('Unexpected error.')
}
// this call is used to test that headers are being sent.
})
})
})

after(done => ipfsd.stop(done))
})