Socket
Socket
Sign inDemoInstall

popsicle

Package Overview
Dependencies
Maintainers
1
Versions
99
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

popsicle - npm Package Compare versions

Comparing version 0.3.11 to 0.4.0

2

package.json
{
"name": "popsicle",
"version": "0.3.11",
"version": "0.4.0",
"description": "Simple HTTP requests for node and the browser",

@@ -5,0 +5,0 @@ "main": "popsicle.js",

/* global define */
(function () {
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory)
} else if (typeof exports === 'object') {
module.exports = factory()
} else {
root.popsicle = factory()
}
})(this, function () {
var isNode = typeof window === 'undefined'

@@ -18,2 +26,4 @@ var root = isNode ? global : window

var abortRequest
var createRequest
var parseRawHeaders

@@ -50,3 +60,2 @@

*
* @param {Object} obj
* @param {String} property

@@ -56,20 +65,14 @@ * @param {String} callback

*/
function setProgress (obj, property, callback) {
var method = '_set' + property.charAt(0).toUpperCase() + property.slice(1)
/**
* Create the progress update method.
*
* @param {Number} num
*/
obj[method] = function (num) {
if (this[property] === num) {
function setProgress (property, callback) {
return function (req, num) {
if (req[property] === num) {
return
}
this[property] = num
req[property] = num
this[callback]()
this._completed()
this._emitProgress()
callback(req)
req.completed = (req.uploaded + req.downloaded) / 2
emitProgress(req, req._progress)
}

@@ -127,14 +130,2 @@ }

/**
* Create a stream error instance.
*
* @param {Popsicle} self
* @return {Error}
*/
function streamError (self) {
var err = self.error('Request is streaming')
err.stream = true
return err
}
/**
* Create a timeout error instance.

@@ -146,14 +137,16 @@ *

function abortError (self) {
var timeout = self.timeout
var err
if (self._error) {
return self._error
}
if (self.timedout) {
err = self.error('Timeout of ' + timeout + 'ms exceeded')
err.timeout = timeout
} else {
err = self.error('Request aborted')
err.abort = true
if (!self.timedout) {
var abortedError = self.error('Request aborted')
abortedError.abort = true
return abortedError
}
return err
var timeout = self.timeout
var timedoutError = self.error('Timeout of ' + timeout + 'ms exceeded')
timedoutError.timeout = timeout
return timedoutError
}

@@ -417,3 +410,3 @@

* @param {Response} response
* @return {Response}
* @return {Promise}
*/

@@ -437,6 +430,4 @@ function parseResponse (response) {

} catch (e) {
throw parseError(response, e)
return Promise.reject(parseError(response, e))
}
return response
}

@@ -473,2 +464,26 @@

/**
* Remove all listener functions.
*
* @param {Request} request
*/
function removeListeners (request) {
delete request._before
delete request._after
delete request._always
delete request._progress
}
/**
* Check if the request has been aborted before starting.
*
* @param {Request} request
* @return {Promise}
*/
function checkAborted (request) {
if (request.aborted) {
return Promise.reject(abortError(request))
}
}
/**
* Set headers on an instance.

@@ -504,2 +519,186 @@ *

/**
* Set the request to error outside the normal request execution flow.
*
* @param {Request} req
* @param {Error} err
*/
function errored (req, err) {
req._error = err
req.abort()
}
/**
* Emit a request progress event (upload or download).
*
* @param {Array<Function>} fns
*/
function emitProgress (req, fns) {
if (!fns || req._error) {
return
}
try {
for (var i = 0; i < fns.length; i++) {
fns[i](req)
}
} catch (e) {
errored(req, e)
}
}
/**
* Set upload progress properties.
*
* @type {Function}
* @param {Number} num
*/
var setUploadSize = setProgress('uploadSize', function (req) {
var n = req.uploaded
var size = req.uploadSize
var total = req.uploadTotal
req.uploaded = calc(n, size, total)
})
/**
* Set download progress properties.
*
* @type {Function}
* @param {Number} num
*/
var setDownloadSize = setProgress('downloadSize', function (req) {
var n = req.downloaded
var size = req.downloadSize
var total = req.downloadTotal
req.downloaded = calc(n, size, total)
})
/**
* Finished uploading.
*
* @param {Request} req
*/
function setUploadFinished (req) {
if (req.uploaded === 1) {
return
}
req.uploaded = 1
req.completed = 0.5
emitProgress(req, req._progress)
}
/**
* Finished downloading.
*
* @param {Request} req
*/
function setDownloadFinished (req) {
if (req.downloaded === 1) {
return
}
req.downloaded = 1
req.completed = 1
emitProgress(req, req._progress)
}
/**
* Create a function for pushing functions onto a stack.
*
* @param {String} prop
* @return {Function}
*/
function pushListener (prop) {
return function (fn) {
if (this.opened) {
throw new Error('Listeners can not be added after request has started')
}
if (typeof fn !== 'function') {
throw new TypeError('Expected a function but got ' + fn)
}
this[prop] = this[prop] || []
this[prop].push(fn)
return this
}
}
/**
* Create a promise chain.
*
* @param {Array} fns
* @param {*} arg
* @return {Promise}
*/
function chain (fns, arg) {
return fns.reduce(function (promise, fn) {
return promise.then(function () {
return fn(arg)
})
}, Promise.resolve())
}
/**
* Setup the request instance.
*
* @param {Request} self
*/
function setup (self) {
var timeout = self.timeout
if (timeout) {
self._timer = setTimeout(function () {
self.timedout = true
self.abort()
}, timeout)
}
// Set the request to "opened", disables any new listeners.
self.opened = true
return chain(self._before, self)
.then(function () {
return createRequest(self)
})
.then(function (response) {
response.request = self
self.response = response
return chain(self._after, response)
})
.catch(function (err) {
function reject () {
return Promise.reject(err)
}
return chain(self._always, self).then(reject)
})
.then(function () {
return chain(self._always, self)
})
.then(function () {
return self.response
})
}
/**
* Create the HTTP request promise.
*
* @param {Request} self
* @return {Promise}
*/
function create (self) {
// Setup a new promise request if none exists.
if (!self._promise) {
self._promise = setup(self)
}
return self._promise
}
/**
* Keep track of headers in a single instance.

@@ -586,11 +785,6 @@ */

*
* @param {Object} options
* @param {Request} request
*/
function Response (raw, request) {
function Response (request) {
Headers.call(this)
this.raw = raw
this.request = request
request.response = this
}

@@ -690,2 +884,3 @@

// Request state.
this.opened = false
this.aborted = false

@@ -702,2 +897,11 @@

}
this.before(checkAborted)
this.before(defaultAccept)
this.before(stringifyRequest)
this.before(correctType)
this.after(parseResponse)
this.always(removeListeners)
}

@@ -728,3 +932,3 @@

/**
* Track request completion progress.
* Track various request states.
*

@@ -734,101 +938,8 @@ * @param {Function} fn

*/
Request.prototype.progress = function (fn) {
if (this.completed) {
return this
}
Request.prototype.before = pushListener('_before')
Request.prototype.after = pushListener('_after')
Request.prototype.always = pushListener('_always')
Request.prototype.progress = pushListener('_progress')
this._progressFns = this._progressFns || []
this._progressFns.push(fn)
return this
}
/**
* Set upload progress properties.
*
* @private
* @type {Function}
* @param {Number} num
*/
setProgress(Request.prototype, 'uploadSize', '_uploaded')
setProgress(Request.prototype, 'downloadSize', '_downloaded')
/**
* Calculate the uploaded percentage.
*/
Request.prototype._uploaded = function () {
var n = this.uploaded
var size = this.uploadSize
var total = this.uploadTotal
this.uploaded = calc(n, size, total)
}
/**
* Calculate the downloaded percentage.
*/
Request.prototype._downloaded = function () {
var n = this.downloaded
var size = this.downloadSize
var total = this.downloadTotal
this.downloaded = calc(n, size, total)
}
/**
* Update the completed percentage.
*/
Request.prototype._completed = function () {
this.completed = (this.uploaded + this.downloaded) / 2
}
/**
* Emit a request progress event (upload or download).
*/
Request.prototype._emitProgress = function () {
var fns = this._progressFns
if (!fns || this._error) {
return
}
try {
for (var i = 0; i < fns.length; i++) {
fns[i](this)
}
} catch (e) {
this._errored(e)
}
}
/**
* Finished uploading.
*/
Request.prototype._uploadFinished = function () {
if (this.uploaded === 1) {
return
}
this.uploaded = 1
this.completed = 0.5
this._emitProgress()
}
/**
* Finished downloading.
*/
Request.prototype._downloadFinished = function () {
if (this.downloaded === 1) {
return
}
this.downloaded = 1
this.completed = 1
this._emitProgress()
}
/**
* Allows request plugins.

@@ -845,48 +956,2 @@ *

/**
* Setup the request instance (promises and streams).
*/
Request.prototype._setup = function () {
var self = this
var timeout = this.timeout
this.use(defaultAccept)
this.use(stringifyRequest)
this.use(correctType)
this.progress(function (e) {
if (e.completed === 1) {
delete self._progressFns
}
})
if (timeout) {
this._timer = setTimeout(function () {
self.timedout = true
self.abort()
}, timeout)
}
}
/**
* Trigger the HTTP request.
*
* @return {Promise}
*/
Request.prototype.create = function () {
// Setup a new promise request if none exists.
if (!this._promise) {
// If already aborted, create a rejected promise.
if (this.aborted) {
this._promise = Promise.reject(abortError(this))
} else {
this._setup()
this._promise = this._create().then(parseResponse)
}
}
return this._promise
}
/**
* Abort request.

@@ -907,4 +972,4 @@ *

// Abort and emit the final progress event.
this._abort()
this._emitProgress()
abortRequest(this)
emitProgress(this, this._progress)
clearTimeout(this._timer)

@@ -916,12 +981,2 @@

/**
* Trigger a request-related error that should break requests.
*
* @param {Error} err
*/
Request.prototype._errored = function (err) {
this._error = err
this.abort()
}
/**
* Create a popsicle error instance.

@@ -946,3 +1001,3 @@ *

cb(null, value)
}, cb)
}).catch(cb)
}

@@ -958,3 +1013,3 @@

Request.prototype.then = function (onFulfilled, onRejected) {
return this.create().then(onFulfilled, onRejected)
return create(this).then(onFulfilled, onRejected)
}

@@ -965,7 +1020,7 @@

*
* @param {Function} cb
* @param {Function} onRejected
* @return {Promise}
*/
Request.prototype['catch'] = function (onRejected) {
return this.create()['catch'](onRejected)
return this.then(null, onRejected)
}

@@ -1040,4 +1095,2 @@

var trackRequestProgress = function (self, request) {
self._request = request
function onRequest (request) {

@@ -1050,3 +1103,3 @@ var write = request.write

request.write = function (data) {
self._setUploadSize(self.uploadSize + byteLength(data))
setUploadSize(self, self.uploadSize + byteLength(data))

@@ -1060,3 +1113,3 @@ return write.apply(this, arguments)

self.downloadTotal = num(response.headers['content-length'])
self._uploadFinished()
setUploadFinished(self)
}

@@ -1066,3 +1119,3 @@

// Data should always be a `Buffer` instance.
self._setDownloadSize(self.downloadSize + data.length)
setDownloadSize(self, self.downloadSize + data.length)
}

@@ -1101,12 +1154,6 @@

*
* @param {Request} self
* @return {Promise}
*/
Request.prototype._create = function () {
var self = this
// Throw on promise creation if streaming.
if (this._stream) {
throw streamError(this)
}
createRequest = function (self) {
return new Promise(function (resolve, reject) {

@@ -1118,3 +1165,3 @@ var opts = requestOptions(self)

delete self._request
self._downloadFinished()
setDownloadFinished(self)

@@ -1130,3 +1177,3 @@ if (err) {

var res = new Response(response, self)
var res = new Response()

@@ -1141,9 +1188,6 @@ res.body = response.body

req.on('abort', function () {
if (self._error) {
return reject(self._error)
}
return reject(abortError(self))
})
self._request = req
trackRequestProgress(self, req)

@@ -1156,39 +1200,9 @@ })

*
* @return {Request}
* @param {Request} self
*/
Request.prototype._abort = function () {
if (this._request) {
this._request.abort()
abortRequest = function (self) {
if (self._request) {
self._request.abort()
}
}
/**
* Expose the current request stream.
*
* @return {Object}
*/
Request.prototype.stream = function () {
if (!this._stream) {
this._setup()
// Initialize a streaming request instance.
// TODO: Emit a stream error if already aborted.
// TODO: Catch stream errors and coerce to popsicle errors.
var req = this._stream = request(requestOptions(this))
trackRequestProgress(this, req)
}
return this._stream
}
/**
* Pipe the current response into another stream.
*
* @param {Object} stream
* @return {Object}
*/
Request.prototype.pipe = function (stream) {
return this.stream().pipe(stream)
}
} else {

@@ -1240,10 +1254,11 @@ /**

*
* @param {Request} self
* @return {Promise}
*/
Request.prototype._create = function () {
var self = this
var url = self.fullUrl()
var method = self.method
createRequest = function (self) {
return new Promise(function (resolve, reject) {
var url = self.fullUrl()
var method = self.method
var res = new Response()
return new Promise(function (resolve, reject) {
// Loading HTTP resources from HTTPS is restricted and uncatchable.

@@ -1256,4 +1271,2 @@ if (window.location.protocol === 'https:' && /^http\:/.test(url)) {

var res = new Response(xhr, self)
xhr.onreadystatechange = function () {

@@ -1271,3 +1284,3 @@ if (xhr.readyState === 2) {

// `xhr` object invalid.
self._uploadFinished()
setUploadFinished(self)
}

@@ -1278,8 +1291,4 @@

delete self._xhr
self._downloadFinished()
setDownloadFinished(self)
if (self._error) {
return reject(self._error)
}
// Handle the aborted state internally, PhantomJS doesn't reset

@@ -1307,3 +1316,3 @@ // `xhr.status` to zero on abort.

self._setDownloadSize(e.loaded)
setDownloadSize(self, e.loaded)
}

@@ -1316,3 +1325,3 @@

self.uploadTotal = 0
self._setUploadSize(0)
setUploadSize(self, 0)
} else {

@@ -1324,3 +1333,3 @@ xhr.upload.onprogress = function (e) {

self._setUploadSize(e.loaded)
setUploadSize(self, e.loaded)
}

@@ -1352,6 +1361,8 @@ }

* Abort a running XMLHttpRequest.
*
* @param {Request} self
*/
Request.prototype._abort = function () {
if (this._xhr) {
this._xhr.abort()
abortRequest = function (self) {
if (self._xhr) {
self._xhr.abort()
}

@@ -1401,3 +1412,3 @@ }

popsicle.jar = function () {
throw new Error('Cookie jars are not supported in browsers')
throw new Error('Cookie jars are not supported on the browser')
}

@@ -1412,11 +1423,3 @@ }

if (typeof define === 'function' && define.amd) {
define([], function () {
return popsicle
})
} else if (typeof exports === 'object') {
module.exports = popsicle
} else {
root.popsicle = popsicle
}
})()
return popsicle
})

@@ -161,5 +161,19 @@ # ![Popsicle](https://cdn.rawgit.com/blakeembrey/popsicle/master/logo.svg)

#### Cookie Jar (Node only)
You can create a reusable cookie jar instance for requests by calling `popsicle.jar`.
```javascript
var jar = request.jar();
request({
method: 'POST',
url: '/users',
jar: jar
});
```
### Handling Responses
Popsicle responses can be handled in multiple ways. Promises, node-style callbacks and streams (node only) are all supported.
Promises and node-style callbacks are supported.

@@ -195,27 +209,2 @@ #### Promises

#### Streams (Node only)
**Incomplete: Emits incorrect errors**
On node, you can also chain using streams.
```javascript
request('/users')
.pipe(fs.createWriteStream('users.json'));
```
#### Cookie Jar (Node only)
You can create a reusable cookie jar instance for requests by calling `popsicle.jar`.
```javascript
var jar = request.jar();
request({
method: 'POST',
url: '/users',
jar: jar
});
```
### Response Objects

@@ -234,2 +223,3 @@

* **get(key)** Retrieve a HTTP header using a case-insensitive key
* **name(key)** Retrieve the original HTTP header name using a case-insensitive key
* **type()** Return the response type (E.g. `application/json`)

@@ -250,3 +240,3 @@

A simple plugin interface is exposed through `Request#use` and promises.
A simple plugin interface is exposed through `Request#use`.

@@ -262,10 +252,10 @@ #### Existing Plugins

#### Using Plugins
#### Creating Plugins
Plugins should expose a single function that accepts a `Request` instance. For example:
Plugins must be a function that accepts configuration and returns another function. For example, here's a basic URL prefix plugin.
```javascript
function prefix (uri) {
function prefix (url) {
return function (req) {
req.url = uri + req.url;
req.url = url + req.url;
};

@@ -281,2 +271,8 @@ }

If you need to augment the request or response lifecycle, there are a number of functions you can register. All listeners accept an optional promise that will resolve before proceeding.
* **before(fn)** Register a function to run before the request is made
* **after(fn)** Register a function to receive the response object
* **always(fn)** Register a function that always runs on `resolve` or `reject`
## Development and Testing

@@ -286,3 +282,3 @@

```bash
```
npm install && npm test

@@ -293,5 +289,5 @@ ```

* [Superagent](https://github.com/visionmedia/superagent) - HTTP requests on node and browser
* [Superagent](https://github.com/visionmedia/superagent) - HTTP requests for node and browsers
* [Fetch](https://github.com/github/fetch) - Browser polyfill for promise-based HTTP requests
* [Axios](https://github.com/mzabriskie/axios) - Similar API based on Angular's $http service
* [Axios](https://github.com/mzabriskie/axios) - HTTP request API based on Angular's $http service

@@ -298,0 +294,0 @@ ## License

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc