http
HTTP is a standard protocol on top of the TCP transport protocol. It retries by default, and is widely supported. Unlike many other programming languages, Node ships with its own HTTP client as part of the core library. Given that Node was originally intended as a JS version of Nginx it should not be surprising that Node is quite good at HTTP.
require('http')
in Node is often misunderstood, a lot of people never try it
out choosing to interface with HTTP through frameworks such as express
instead. Once you get past the somewhat rough documentation it turns out
require('http')
is very versatile and only has a tiny API surface.
In this section I'll show some of the more common patterns using Node's HTTP module.
Server
Basic
const http = require('http')
const port = 1337
http.createServer(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('hello world')
}).listen(port)
Listen on random unused port & retrieve port
const getPort = require('get-server-port')
const http = require('http')
const server = http.createServer(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('hello world')
})
server.listen()
const port = getPort(server)
Stream a file as a server response
const http = require('http')
const fs = require('fs')
http.createServer(function (req, res) {
res.setHeader('Content-Type', 'application/json')
fs.createReadStream('./file.json').pipe(res)
}).listen()
Switch on a url
A statusCode of 200
is the default. When no path is matched a 404
should be
returned.
const http = require('http')
const url = require('url')
http.createServer(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
const path = url.parse(req.url)
// minimal router
if (path === '/hello') return res.end('hello')
if (path === '/error') return res.end('oh no!')
res.statusCode = 404
res.end('path not found')
}).listen()
Client
GET request
const http = require('http')
// create a request and pipe the body to stdout
http.get('http://google.com', function (res) {
res.pipe(process.stdout)
})
const concat = require('concat-stream')
const http = require('http')
// create a request and parse the body
http.get('http://google.com', function (res) {
res.pipe(concat({ string: true }, function (str) {
process.stdout.write('status' + res.statusCode + '\n')
process.stdout.write(str + '\n')
}))
})
POST request
const concat = require('concat-stream')
const http = require('http')
// create a 'POST' request
const opts = { href: 'http://google.com', method: 'POST' }
const req = http.request(opts, res => res.pipe(process.stdout))
// pipe data into the 'POST' request. Once all data is
// passed the request will be sent off, equivalent to req.end().
fs.createReadStream('./foo.txt').pipe(req)
Request url parsing
The url
module synergizes extremely well with the http
module. In order to
parse urls and destructure query strings do:
const http = require('http')
const url = require('url')
http.createServer((req, res) => {
// pass true to url.parse to also destructure the query object
const url = url.parse(req.url, true)
console.log(url)
res.end()
}).listen()
headers
Get a header:
http.createServer(function (req, res) {
console.log(req.headers['etag'])
req.end()
}).listen()
Get a cookie:
const Cookies = require('cookies')
http.createServer(function (req, res) {
const cookies = cookies(req, res)
const cookie = cookies.get('my-cookies')
req.end(cookie ? 'cookie found' : 'no cookie found')
}).listen()
Request to localhost
const opts = {
protocol: 'http:',
hostname: 'localhost',
port: conf.port,
path: '/bundle.js',
method: 'GET'
}
const req = http.request(opts, function (res) {
t.equal(res.statusCode, 200, 'status was OK')
t.equal(res.headers['content-type'], 'application/javascript', 'type is JS')
})
req.end()
Download multipart file
Now this is super icky. Apparently busboy
is fast, but it's also not pretty.
I'd very much like to find a better approach:
// somehow busboy likes to throw errors if incorrect headers are passed :/
try {
var busboy = new Busboy({ headers: req.headers })
} catch (e) {
console.error(e)
}
busboy.on('file', function (fieldname, file, filename) {
file.pipe(process.stdout)
})
req.pipe(busboy)
Create multipart request
const FormData = require('form-data')
const http = require('http')
const opts = {
protocol: 'http:',
hostname: 'localhost',
port: config.API_PORT,
path: '/torrent/test_image.jpg',
headers: {
'Content-type': 'multipart/form-data; boundary=XXX'
},
method: 'POST'
}
const req = http.request(opts)
const filePath = path.join(__dirname, '../resources/test_image.jpg')
const rs = fs.createReadStream(filePath)
const form = new FormData()
form.append('upload', rs)
form.pipe(req)