Skip to content

Instantly share code, notes, and snippets.

@osiyuk
Created November 13, 2016 23:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save osiyuk/bcd1dc76c0603b964b2e4ed838ffc471 to your computer and use it in GitHub Desktop.
Save osiyuk/bcd1dc76c0603b964b2e4ed838ffc471 to your computer and use it in GitHub Desktop.
lightweight fetch, custom promise, native http, plain callbacks
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
// inspired by https://github.com/cujojs/rest/blob/master/client/node.js
'use strict';
// allow custom promises
if (!global.Promise) {
throw new TypeError('native promise missing, set global.Promise to your \
favorite alternative');
}
// https://github.com/bitinn/node-fetch#api
function fetch(url, options) {
var parsed,
headers = {},
request;
// 1) fetch(options)
if (typeof url === 'object' && typeof options === 'undefined') {
options = url;
url = '';
}
// 2) fetch(url, [options])
options = options || {};
if (typeof url !== 'string' && typeof options !== 'object')
throw new Error('Usage: fetch(options) or fetch(url, [options])');
// https://nodejs.org/docs/latest-v0.10.x/api/url.html#url_url
parsed = require('url').parse(url);
// https://nodejs.org/docs/latest-v0.10.x/api/http.html#http_http_request_options_callback
options.hostname = options.hostname || options.host || parsed.hostname;
if (typeof options.hostname === 'undefined') {
throw new Error('At least `host` or `hostname` should be provided');
}
if (parsed.port) options.port = options.port || parsed.port;
options.method = options.method || (options.body ? 'POST' : 'GET');
options.path = options.path || parsed.path;
if (typeof options.path === 'undefined') {
throw new Error('You should provide `path` directly');
}
Object.keys(options.headers || {}).forEach(function (name) {
// TODO implement normalizeHeaderName
headers[normalizeHeaderName(name)] = options.headers[name];
});
options.headers = headers;
request = {
options: options,
body: options.body || ''
};
return new Promise(httpResolver.bind(request));
}
// http://dfilatov.github.io/vow/
// https://github.com/dfilatov/vow#creating-a-promise
function httpResolver(resolve, reject) {
var req;
req = require('http').request(this.options, onResponse.bind(this));
// https://nodejs.org/docs/latest-v0.10.x/api/events.html#events_emitter_on_event_listener
req.on('error', onError);
// https://nodejs.org/docs/latest-v0.10.x/api/http.html#http_request_write_chunk_encoding
// TODO use chunked encoding, if body.length exceeds PAGESIZE
req.write(this.body, 'utf8');
// https://nodejs.org/docs/latest-v0.10.x/api/http.html#http_request_end_data_encoding
req.end();
this.resolve = resolve;
this.reject = reject;
}
// https://nodejs.org/docs/latest-v0.10.x/api/http.html#http_event_response
function onResponse(response) {
this.buffers = [];
// https://nodejs.org/docs/latest-v0.10.x/api/http.html#http_http_incomingmessage
this.response = {
status: response.statusCode,
headers: response.headers
};
response.on('data', onData.bind(this));
response.on('end', onEnd.bind(this));
}
function onError(err) {
this.response.error = err;
this.reject(this.response);
}
// https://nodejs.org/docs/latest-v0.10.x/api/stream.html#stream_event_data
function onData(chunk) {
// collect next Buffer chunk
this.buffers.push(chunk);
}
// https://nodejs.org/docs/latest-v0.10.x/api/stream.html#stream_event_end
function onEnd() {
var response = this.response,
hasBody = this.buffers.length > 0;
response.body = hasBody ? Buffer.concat(this.buffers).toString() : '';
this.resolve(response);
}
module.exports.fetch = fetch;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment