Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

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 11.0.0-0 to 11.0.0-1

8

dist/common.d.ts
import { Request, Response } from 'servie';
import { Middleware } from 'throwback';
/**
* Request normalization.
*/
export interface NormalizeRequestOptions {
upgradeInsecureRequests?: boolean;
}
/**
* Default header handling.
*/
export declare function normalizeRequest<T extends Request, U extends Response>(): Middleware<T, U>;
export declare function normalizeRequest<T extends Request, U extends Response>(options?: NormalizeRequestOptions): Middleware<T, U>;
/**

@@ -8,0 +14,0 @@ * Redirect middleware configuration.

94

dist/common.js

@@ -16,2 +16,26 @@ "use strict";

/**
* Default header handling.
*/
function normalizeRequest(options = {}) {
return function (req, next) {
// Block requests when already aborted.
if (req.aborted)
return Promise.reject(new error_1.PopsicleError('Request aborted', 'EABORT', req));
// Remove headers that should not be created by the user.
req.headers.delete('Host');
// If we have no accept header set already, default to accepting
// everything. This is needed because otherwise Firefox defaults to
// an accept header of `html/xml`.
if (!req.headers.get('Accept')) {
req.headers.set('Accept', '*/*');
}
// Request a preference to upgrade to HTTPS.
if (options.upgradeInsecureRequests !== false && req.Url.protocol === 'http:') {
req.headers.set('Upgrade-Insecure-Requests', '1');
}
return next();
};
}
exports.normalizeRequest = normalizeRequest;
/**
* Redirection types to handle.

@@ -35,22 +59,2 @@ */

/**
* Default header handling.
*/
function normalizeRequest() {
return function (req, next) {
// Block requests when already aborted.
if (req.aborted)
return Promise.reject(new error_1.PopsicleError('Request aborted', 'EABORT', req));
// If we have no accept header set already, default to accepting
// everything. This is needed because otherwise Firefox defaults to
// an accept header of `html/xml`.
if (!req.headers.get('Accept')) {
req.headers.set('Accept', '*/*');
}
// Remove headers that should never be set by the user.
req.headers.delete('Host');
return next(req);
};
}
exports.normalizeRequest = normalizeRequest;
/**
* Middleware function for following HTTP redirects.

@@ -68,32 +72,30 @@ */

const redirect = REDIRECT_STATUS[res.statusCode];
// Handle HTTP redirects.
if (redirect !== undefined && res.headers.has('Location')) {
const newUrl = url_1.resolve(req.url, res.headers.get('Location')); // tslint:disable-line
// Ignore the result of the response on redirect.
req.abort();
req.events.emit('redirect', newUrl);
if (redirect === REDIRECT_TYPE.FOLLOW_WITH_GET) {
if (redirect === undefined || !res.headers.has('Location'))
return res;
const newUrl = url_1.resolve(req.url, res.headers.get('Location')); // tslint:disable-line
// Ignore the result of the response on redirect.
req.abort();
req.events.emit('redirect', newUrl);
if (redirect === REDIRECT_TYPE.FOLLOW_WITH_GET) {
req = initialRequest.clone();
req.headers.set('Content-Length', '0');
req.url = newUrl;
req.method = req.method.toUpperCase() === 'HEAD' ? 'HEAD' : 'GET';
req.body = universal_1.createBody(undefined);
req.trailer = Promise.resolve(servie_1.createHeaders());
continue;
}
if (redirect === REDIRECT_TYPE.FOLLOW_WITH_CONFIRMATION) {
const method = req.method.toUpperCase();
// Following HTTP spec by automatically redirecting with GET/HEAD.
if (method === 'GET' || method === 'HEAD') {
req = initialRequest.clone();
req.headers.set('Content-Length', '0');
req.url = newUrl;
req.method = req.method.toUpperCase() === 'HEAD' ? 'HEAD' : 'GET';
req.body = universal_1.createBody(undefined);
req.trailer = Promise.resolve(servie_1.createHeaders());
continue;
}
if (redirect === REDIRECT_TYPE.FOLLOW_WITH_CONFIRMATION) {
const method = req.method.toUpperCase();
// Following HTTP spec by automatically redirecting with GET/HEAD.
if (method === 'GET' || method === 'HEAD') {
req = initialRequest.clone();
req.url = newUrl;
continue;
}
// Allow the user to confirm redirect according to HTTP spec.
if (confirmRedirect(req, res)) {
req = initialRequest.clone();
req.url = newUrl;
continue;
}
return res;
// Allow the user to confirm redirect according to HTTP spec.
if (confirmRedirect(req, res)) {
req = initialRequest.clone();
req.url = newUrl;
continue;
}

@@ -100,0 +102,0 @@ }

@@ -36,3 +36,3 @@ "use strict";

const res = yield node_1.transport()(node_1.request(`${common_spec_1.TEST_HTTP_URL}/echo/header/user-agent`));
expect(yield res.body.text()).toMatch(/^Popsicle \(https:\/\/github\.com\/blakeembrey\/popsicle\)$/);
expect(yield res.body.text()).toEqual('Popsicle (https://github.com/serviejs/popsicle)');
}));

@@ -39,0 +39,0 @@ it('send a custom user agent header', () => __awaiter(this, void 0, void 0, function* () {

@@ -5,5 +5,6 @@ /// <reference types="node" />

import { Agent } from 'http';
import { SecureContext } from 'tls';
import { Request, Response, ResponseOptions } from 'servie';
import { Body } from 'servie/dist/body/node';
import { FollowRedirectsOptions } from '../common';
import { FollowRedirectsOptions, NormalizeRequestOptions } from '../common';
/**

@@ -13,3 +14,5 @@ * Extend response with URL.

export interface HttpResponseOptions extends ResponseOptions {
url: string;
body: Body;
httpVersion: string;
}

@@ -20,5 +23,9 @@ /**

export declare class HttpResponse extends Response implements HttpResponseOptions {
url: string;
body: Body;
httpVersion: string;
constructor(options: HttpResponseOptions);
}
export declare class Http2Response extends HttpResponse {
}
/**

@@ -43,5 +50,11 @@ * Cookie middleware configuration.

/**
* Override the default user-agent header.
*/
export interface NormalizeUserAgentOptions {
userAgent?: string;
}
/**
* Set default user-agent in node.js.
*/
export declare function normalizeUserAgent<T extends Request, U extends Response>(): Middleware<T, U>;
export declare function normalizeUserAgent<T extends Request, U extends Response>(options?: NormalizeUserAgentOptions): Middleware<T, U>;
/**

@@ -51,3 +64,3 @@ * Node.js HTTP request options.

export interface SendOptions {
agent?: Agent;
negotiateHttp2?: boolean;
rejectUnauthorized?: boolean;

@@ -57,2 +70,4 @@ ca?: string | Buffer | Array<string | Buffer>;

key?: string | Buffer;
agent?: Agent;
secureContext?: SecureContext;
secureProtocol?: string;

@@ -67,6 +82,6 @@ }

*/
export interface TransportOptions extends SendOptions, FollowRedirectsOptions, SendOptions {
jar?: CookieJar;
unzip?: boolean;
follow?: boolean;
export interface TransportOptions extends SendOptions, FollowRedirectsOptions, SendOptions, NormalizeRequestOptions, NormalizeUserAgentOptions {
jar?: CookieJar | false;
unzip?: false;
follow?: false;
}

@@ -73,0 +88,0 @@ /**

@@ -12,5 +12,9 @@ "use strict";

const pump = require("pump");
const tough_cookie_1 = require("tough-cookie");
const throwback_1 = require("throwback");
const http_1 = require("http");
const https_1 = require("https");
const net_1 = require("net");
const tls_1 = require("tls");
const http2_1 = require("http2");
const stream_1 = require("stream");

@@ -20,3 +24,2 @@ const zlib_1 = require("zlib");

const node_1 = require("servie/dist/body/node");
const url_1 = require("url");
const error_1 = require("../error");

@@ -30,6 +33,11 @@ const common_1 = require("../common");

super(options);
this.url = options.url;
this.body = options.body;
this.httpVersion = options.httpVersion;
}
}
exports.HttpResponse = HttpResponse;
class Http2Response extends HttpResponse {
}
exports.Http2Response = Http2Response;
/**

@@ -97,7 +105,7 @@ * Read cookies from the cookie jar.

*/
function normalizeUserAgent() {
function normalizeUserAgent(options = {}) {
const userAgent = options.userAgent || 'Popsicle (https://github.com/serviejs/popsicle)';
return function (req, next) {
if (!req.headers.has('User-Agent')) {
req.headers.set('User-Agent', 'Popsicle (https://github.com/blakeembrey/popsicle)');
}
if (!req.headers.has('User-Agent'))
req.headers.set('User-Agent', userAgent);
return next();

@@ -108,2 +116,140 @@ };

/**
* Write Servie body object to node.js stream.
*/
function sendBody(body, stream, onError) {
// Pipe the body to the stream.
if (body instanceof node_1.Body) {
if (body.buffered) {
body.buffer().then(x => stream.end(x), onError);
}
else {
pump(body.stream(), stream);
}
}
else {
body.arrayBuffer().then(x => stream.end(Buffer.from(x)), onError);
}
}
/**
* Execute HTTP request.
*/
function execHttp(req, protocol, host, port, socket, agent) {
return new Promise((resolve, reject) => {
const { url, body, Url } = req;
const encrypted = Url.protocol === 'https:';
const request = encrypted ? https_1.request : http_1.request;
const arg = {
protocol,
host,
port,
method: req.method,
path: Url.path,
headers: req.headers.asObject(false),
auth: Url.auth,
agent,
createConnection: () => socket
};
const rawRequest = request(arg);
const requestStream = new stream_1.PassThrough();
// Trigger unavailable error when node.js errors before response.
function onError(err) {
return reject(new error_1.PopsicleError('Unable to connect', 'EUNAVAILABLE', req, err));
}
// Track the node.js response.
function onResponse(rawResponse) {
const { statusCode, statusMessage, httpVersion } = rawResponse;
const headers = servie_1.createHeaders(rawResponse.rawHeaders);
const body = node_1.createBody(pump(rawResponse, new stream_1.PassThrough()), { headers: {} });
// Trailers are populated on "end".
const trailer = new Promise(resolve => {
rawResponse.on('end', () => resolve(servie_1.createHeaders(rawResponse.rawTrailers)));
});
// Replace request error listener behaviour.
rawRequest.removeListener('error', onError);
rawRequest.on('error', err => req.events.emit('error', err));
const { address: localAddress, port: localPort } = rawRequest.connection.address();
const { address: remoteAddress, port: remotePort } = rawResponse.connection.address();
const res = new HttpResponse({ statusCode, statusMessage, headers, trailer, body, url, httpVersion });
// Update request connection.
req.connection = { localAddress, localPort, remoteAddress, remotePort, encrypted };
// https://github.com/serviejs/servie#implementers
res.started = true;
req.events.emit('response', res);
// Track response progress.
rawResponse.on('data', (chunk) => res.bytesTransferred += chunk.length);
rawResponse.on('end', () => res.finished = true);
rawResponse.on('close', () => req.closed = true);
return resolve(res);
}
rawRequest.once('error', onError);
rawRequest.once('response', onResponse);
// https://github.com/serviejs/servie#implementers
req.started = true;
req.events.on('abort', () => rawRequest.abort());
// Track request upload progress.
requestStream.on('data', (chunk) => req.bytesTransferred += chunk.length);
pump(requestStream, rawRequest, () => req.finished = true);
return sendBody(body, requestStream, reject);
});
}
/**
* Execute a HTTP2 connection.
*/
function execHttp2(req, protocol, host, port, socket) {
return new Promise((resolve, reject) => {
// HTTP2 formatted headers.
const headers = Object.assign(req.headers.asObject(false), {
[http2_1.constants.HTTP2_HEADER_METHOD]: req.method,
[http2_1.constants.HTTP2_HEADER_PATH]: req.Url.path
});
// TODO: Fix node.js types.
const connectOptions = {
createConnection: () => socket
};
const authority = `${protocol}//${host}:${port}`;
const client = http2_1.connect(authority, connectOptions);
const rawRequest = client.request(headers, { endStream: false });
const requestStream = new stream_1.PassThrough();
// Trigger unavailable error when node.js errors before response.
function onError(err) {
return reject(new error_1.PopsicleError('Unable to connect', 'EUNAVAILABLE', req, err));
}
rawRequest.on('error', onError);
function onResponse(headers) {
const { localAddress, localPort, remoteAddress, remotePort, encrypted } = socket;
// Replace request error listener behaviour with proxy.
rawRequest.removeListener('error', onError);
rawRequest.on('error', err => req.events.emit('error', err));
const res = new Http2Response({
statusCode: Number(headers[http2_1.constants.HTTP2_HEADER_STATUS]),
url: req.url,
httpVersion: '2.0',
headers: servie_1.createHeaders(headers),
body: node_1.createBody(pump(rawRequest, new stream_1.PassThrough()), { headers: {} })
});
// https://github.com/serviejs/servie#implementers
res.started = true;
req.connection = { localAddress, localPort, remoteAddress, remotePort, encrypted };
// Track response progress.
rawRequest.on('data', (chunk) => res.bytesTransferred += chunk.length);
// Close HTTP2 session when request ends.
rawRequest.on('end', () => {
req.closed = true;
res.finished = true;
client.close();
});
return resolve(res);
}
rawRequest.once('error', onError);
rawRequest.once('response', onResponse);
// https://github.com/serviejs/servie#implementers
req.started = true;
req.events.on('abort', () => rawRequest.destroy());
// Track request upload progress.
requestStream.on('data', (chunk) => req.bytesTransferred += chunk.length);
pump(requestStream, rawRequest, () => req.finished = true);
return sendBody(req.body, requestStream, reject);
});
}
/**
* Function to execute HTTP request.

@@ -113,75 +259,47 @@ */

return function (req) {
return new Promise((resolve, reject) => {
const { body } = req;
const arg = url_1.parse(req.url);
const encrypted = arg.protocol === 'https:';
const engine = encrypted ? https_1.request : http_1.request;
// Attach request options.
arg.method = req.method;
arg.headers = req.headers.asObject(false);
arg.agent = options.agent;
arg.rejectUnauthorized = options.rejectUnauthorized !== false;
arg.ca = options.ca;
arg.cert = options.cert;
arg.key = options.key;
arg.secureProtocol = options.secureProtocol;
const rawRequest = engine(arg);
const requestStream = new stream_1.PassThrough();
// Trigger unavailable error when node.js errors before response.
function onError(err) {
reject(new error_1.PopsicleError('Unable to connect', 'EUNAVAILABLE', req, err));
const { hostname, protocol } = req.Url;
const host = hostname || 'localhost';
if (protocol === 'http:') {
const port = Number(req.Url.port) || 80;
const socketOptions = { host, port };
return execHttp(req, protocol, host, port, net_1.connect(socketOptions), options.agent);
}
// Optionally negotiate HTTP2 connection.
if (protocol === 'https:') {
const port = Number(req.Url.port) || 443;
const socketOptions = {
host,
port,
servername: (req.headers.get('host') || host).replace(/:.*$/, ''),
rejectUnauthorized: options.rejectUnauthorized !== false,
ca: options.ca,
cert: options.cert,
key: options.key,
secureProtocol: options.secureProtocol,
secureContext: options.secureContext
};
if (options.negotiateHttp2 === false) {
return execHttp(req, protocol, host, port, tls_1.connect(socketOptions), options.agent);
}
// Track the node.js response.
function onResponse(rawResponse) {
const { statusCode, statusMessage } = rawResponse;
const headers = servie_1.createHeaders(rawResponse.rawHeaders);
const body = node_1.createBody(pump(rawResponse, new stream_1.PassThrough()));
// Trailers are populated on "end".
const trailer = new Promise(resolve => {
rawResponse.on('end', () => resolve(servie_1.createHeaders(rawResponse.rawTrailers)));
// Negotiate for HTTP2 connection over HTTP1.
socketOptions.ALPNProtocols = ['h2', 'http/1.1'];
return new Promise((resolve, reject) => {
const socket = tls_1.connect(socketOptions);
socket.once('secureConnect', () => {
const alpnProtocol = socket.alpnProtocol;
// Successfully negotiated HTTP2 connection.
if (alpnProtocol === 'h2') {
return resolve(execHttp2(req, protocol, host, port, socket));
}
if (alpnProtocol === 'http/1.1') {
return resolve(execHttp(req, protocol, host, port, socket, options.agent));
}
return reject(new error_1.PopsicleError('No ALPN protocol negotiated', 'EALPNPROTOCOL', req));
});
// Replace request error listener behaviour.
rawRequest.removeListener('error', onError);
rawRequest.on('error', err => req.events.emit('error', err));
const { address: localAddress, port: localPort } = rawRequest.connection.address();
const { address: remoteAddress, port: remotePort } = rawResponse.connection.address();
const res = new HttpResponse({ statusCode, statusMessage, headers, trailer, body });
// Update request connection.
req.connection = { localAddress, localPort, remoteAddress, remotePort, encrypted };
// https://github.com/serviejs/servie#implementers
res.started = true;
req.events.emit('response', res);
// Track response progress.
rawResponse.on('data', (chunk) => {
res.bytesTransferred += chunk.length;
socket.once('error', (err) => {
return reject(new error_1.PopsicleError('Unable to connect', 'EUNAVAILABLE', req, err));
});
rawResponse.on('end', () => res.finished = true);
rawResponse.on('close', () => req.closed = true);
return resolve(res);
}
// Track request upload progress.
requestStream.on('data', (chunk) => {
req.bytesTransferred += chunk.length;
});
// Listen for connection errors.
rawRequest.once('error', onError);
rawRequest.once('response', onResponse);
// https://github.com/serviejs/servie#implementers
req.started = true;
req.events.on('abort', () => rawRequest.abort());
// Pump request body into HTTP request object.
pump(requestStream, rawRequest, () => req.finished = true);
// Pipe the body to the stream.
if (body instanceof node_1.Body) {
if (body.buffered) {
body.buffer().then(x => requestStream.end(x), reject);
}
else {
pump(body.stream(), requestStream);
}
}
else {
body.arrayBuffer().then(x => requestStream.end(Buffer.from(x)), reject);
}
});
}
return Promise.reject(new error_1.PopsicleError(`Unsupported URL protocol: ${req.Url.protocol}`, 'EPROTOCOL', req));
};

@@ -194,4 +312,4 @@ }

function transport(options = {}) {
const fns = [common_1.normalizeRequest(), normalizeUserAgent()];
const { jar, unzip = true, follow = true } = options;
const fns = [common_1.normalizeRequest(options), normalizeUserAgent(options)];
const { jar = new tough_cookie_1.CookieJar(), unzip = true, follow = true } = options;
if (unzip)

@@ -198,0 +316,0 @@ fns.push(autoUnzip());

@@ -7,4 +7,4 @@ import { Request, Response, ResponseOptions } from 'servie';

export interface XhrResponseOptions extends ResponseOptions {
url: string;
body: Body;
responseUrl: string;
}

@@ -15,4 +15,4 @@ /**

export declare class XhrResponse extends Response implements XhrResponseOptions {
url: string;
body: Body;
responseUrl: string;
constructor(options: XhrResponseOptions);

@@ -19,0 +19,0 @@ }

@@ -14,4 +14,4 @@ "use strict";

super(options);
this.url = options.url;
this.body = options.body;
this.responseUrl = options.responseUrl;
}

@@ -45,3 +45,3 @@ }

body: browser_1.createBody(type === 'text' ? xhr.responseText : xhr.response, { headers: [] }),
responseUrl: xhr.responseURL
url: xhr.responseURL
});

@@ -48,0 +48,0 @@ // https://github.com/serviejs/servie#implementers

{
"name": "popsicle",
"version": "11.0.0-0",
"version": "11.0.0-1",
"description": "Advanced HTTP requests in node.js and browsers, using Servie",

@@ -5,0 +5,0 @@ "main": "dist/universal.js",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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