Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow monkey patching http / https #1866

Merged
merged 3 commits into from
Aug 2, 2023
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
26 changes: 18 additions & 8 deletions src/net/NodeHttpClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import * as http from 'http';
import * as https from 'https';
import * as http_ from 'http';
import * as https_ from 'https';
import {RequestHeaders, RequestData} from '../Types.js';
import {
HttpClient,
HttpClientResponse,
HttpClientResponseInterface,
} from './HttpClient.js';

// `import * as http_ from 'http'` creates a "Module Namespace Exotic Object"
// which is immune to monkey-patching, whereas http_.default (in an ES Module context)
// will resolve to the same thing as require('http'), which is
// monkey-patchable. We care about this because users in their test
// suites might be using a library like "nock" which relies on the ability
// to monkey-patch and intercept calls to http.request.
const http = ((http_ as unknown) as {default: typeof http_}).default || http_;
richardm-stripe marked this conversation as resolved.
Show resolved Hide resolved
const https =
((https_ as unknown) as {default: typeof https_}).default || https_;

const defaultHttpAgent = new http.Agent({keepAlive: true});
const defaultHttpsAgent = new https.Agent({keepAlive: true});

Expand All @@ -15,9 +25,9 @@ const defaultHttpsAgent = new https.Agent({keepAlive: true});
* requests.`
*/
export class NodeHttpClient extends HttpClient {
_agent?: http.Agent | https.Agent | undefined;
_agent?: http_.Agent | https_.Agent | undefined;

constructor(agent?: http.Agent | https.Agent) {
constructor(agent?: http_.Agent | https_.Agent) {
super();
this._agent = agent;
}
Expand Down Expand Up @@ -93,19 +103,19 @@ export class NodeHttpClient extends HttpClient {

export class NodeHttpClientResponse extends HttpClientResponse
implements HttpClientResponseInterface {
_res: http.IncomingMessage;
_res: http_.IncomingMessage;

constructor(res: http.IncomingMessage) {
constructor(res: http_.IncomingMessage) {
// @ts-ignore
super(res.statusCode, res.headers || {});
this._res = res;
}

getRawResponse(): http.IncomingMessage {
getRawResponse(): http_.IncomingMessage {
return this._res;
}

toStream(streamCompleteCallback: () => void): http.IncomingMessage {
toStream(streamCompleteCallback: () => void): http_.IncomingMessage {
// The raw response is itself the stream, so we just return that. To be
// backwards compatible, we should invoke the streamCompleteCallback only
// once the stream has been fully consumed.
Expand Down
37 changes: 36 additions & 1 deletion testProjects/mjs/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DefaultStripe, {Stripe} from 'stripe';
import assert from 'assert';
import * as http from 'http';

assert(Stripe.PACKAGE_VERSION);
assert(Stripe.USER_AGENT);
Expand Down Expand Up @@ -90,9 +91,43 @@ async function exampleFunction(args) {
}
}


// Test that http is monkey-patchable (motivation: https://github.com/stripe/stripe-node/issues/1844)
async function exampleMonkeyPatchFunction() {
http.default.request = () => {
throw new Error('foo');
};

try {
await stripe.paymentIntents.create({
currency: 'usd',
amount: 2000,
confirm: true,
payment_method: 'pm_card_visa',
});
} catch (e) {
assert (e instanceof stripe.errors.StripeConnectionError);
if (e.detail.message === 'foo') {
return;
} else {
throw e;
}
}

throw new Error('Expected an error');
}

exampleFunction({
// The required parameter currency is missing
amount: 2000,
confirm: true,
payment_method: 'pm_card_visa',
});
}).then(() => {
return exampleMonkeyPatchFunction()
}).then(() => {
process.exit(0);
}).catch((e) => {
console.error(e);
process.exit(1);
}
);
3 changes: 2 additions & 1 deletion tsconfig.cjs.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"noImplicitThis": true,
"strict": true,
"strictFunctionTypes": true,
"types": [ "node" ]
"types": [ "node" ],
"esModuleInterop": false // This is a viral option, do not enable https://www.semver-ts.org/#module-interop
anniel-stripe marked this conversation as resolved.
Show resolved Hide resolved
},
"include": ["./src/**/*"],
"exclude": ["./src/stripe.esm.node.ts", "./src/stripe.esm.worker.ts"],
Expand Down