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

Upgrade to Formidable 3, Node 14, and audit fix #1192

Merged
merged 1 commit into from
Oct 1, 2022
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
2,217 changes: 1,148 additions & 1,069 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"version": "0.45.0",
benasher44 marked this conversation as resolved.
Show resolved Hide resolved
"description": "A minimal node SOAP client",
"engines": {
"node": ">=12.0.0"
"node": ">=14.0.0"
},
"author": "Vinay Pulim <[email protected]>",
"dependencies": {
"axios-ntlm": "^1.2.0",
"debug": "^4.3.2",
"formidable": "^2.0.1",
"formidable": "^3.2.4",
"get-stream": "^6.0.1",
"lodash": "^4.17.21",
"sax": ">=0.6",
Expand All @@ -19,7 +19,7 @@
"xml-crypto": "^2.1.3"
},
"peerDependencies": {
"axios": ">=0.21.1"
"axios": "^0.27.2"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -78,7 +78,7 @@
"source-map-support": "^0.5.10",
"timekeeper": "^2.1.2",
"tslint": "^5.18.0",
"typedoc": "^0.20.37",
"typescript": "^3.9.10"
"typedoc": "^0.23.10",
"typescript": "^4.7.4"
}
}
41 changes: 23 additions & 18 deletions src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as url from 'url';
import { v4 as uuidv4 } from 'uuid';
import MIMEType = require('whatwg-mimetype');
import { gzipSync } from 'zlib';
import { IExOptions, IHeaders, IHttpClient, IMTOMAttachments, IOptions } from './types';
import { IExOptions, IHeaders, IHttpClient, IOptions } from './types';
import { parseMTOMResp } from './utils';

const debug = debugBuilder('node-soap');
Expand Down Expand Up @@ -53,8 +53,6 @@ export class HttpClient implements IHttpClient {
public buildRequest(rurl: string, data: any, exheaders?: IHeaders, exoptions: IExOptions = {}): any {
const curl = url.parse(rurl);
const method = data ? 'POST' : 'GET';
const secure = curl.protocol === 'https:';
const path = [curl.pathname || '/', curl.search || '', curl.hash || ''].join('');

const host = curl.hostname;
const port = parseInt(curl.port, 10);
Expand Down Expand Up @@ -206,7 +204,13 @@ export class HttpClient implements IHttpClient {
}
const _this = this;
req.then((res) => {
let body;

const handleBody = (body?: string) => {
res.data = this.handleResponse(req, res, body || res.data);
callback(null, res, res.data);
return res;
};

if (_this.options.parseReponseAttachments) {
const isMultipartResp = res.headers['content-type'] && res.headers['content-type'].toLowerCase().indexOf('multipart/related') > -1;
if (isMultipartResp) {
Expand All @@ -218,23 +222,24 @@ export class HttpClient implements IHttpClient {
if (!boundary) {
return callback(new Error('Missing boundary from content-type'));
}
const multipartResponse = parseMTOMResp(res.data, boundary);

// first part is the soap response
const firstPart = multipartResponse.parts.shift();
if (!firstPart || !firstPart.body) {
return callback(new Error('Cannot parse multipart response'));
}
body = firstPart.body.toString('utf8');
(res as any).mtomResponseAttachments = multipartResponse;
return parseMTOMResp(res.data, boundary, (err, multipartResponse) => {
if (err) {
return callback(err);
}
// first part is the soap response
const firstPart = multipartResponse.parts.shift();
if (!firstPart || !firstPart.body) {
return callback(new Error('Cannot parse multipart response'));
}
(res as any).mtomResponseAttachments = multipartResponse;
return handleBody(firstPart.body.toString('utf8'));
});
} else {
body = res.data.toString('utf8');
return handleBody(res.data.toString('utf8'));
}
} else {
return handleBody();
}

res.data = this.handleResponse(req, res, body || res.data);
callback(null, res, res.data);
return res;
}, (err) => {
return callback(err);
});
Expand Down
89 changes: 46 additions & 43 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

import * as crypto from 'crypto';
import { MultipartParser } from 'formidable';
import { IMTOMAttachments } from './types';

export function passwordDigest(nonce: string, created: string, password: string): string {
Expand Down Expand Up @@ -67,46 +66,50 @@ export function xmlEscape(obj) {
return obj;
}

export function parseMTOMResp(payload: Buffer, boundary: string): IMTOMAttachments {
const resp: IMTOMAttachments = {
parts: [],
};
let headerName = '';
let headerValue = '';
let data: Buffer;
let partIndex = 0;
const parser = new MultipartParser();

parser.initWithBoundary(boundary);
parser.on('data', ({ name, buffer, start, end }) => {
switch (name) {
case 'partBegin':
resp.parts[partIndex] = {
body: null,
headers: {},
};
data = Buffer.from('');
break;
case 'headerField':
headerName = buffer.slice(start, end).toString();
break;
case 'headerValue':
headerValue = buffer.slice(start, end).toString();
break;
case 'headerEnd':
resp.parts[partIndex].headers[headerName.toLowerCase()] = headerValue;
break;
case 'partData':
data = Buffer.concat([data, buffer.slice(start, end)]);
break;
case 'partEnd':
resp.parts[partIndex].body = data;
partIndex++;
break;
}
});

parser.write(payload);

return resp;
export function parseMTOMResp(payload: Buffer, boundary: string, callback: (err?: Error, resp?: IMTOMAttachments) => void) {
return import('formidable')
.then(({ MultipartParser }) => {
const resp: IMTOMAttachments = {
parts: [],
};
let headerName = '';
let headerValue = '';
let data: Buffer;
let partIndex = 0;
const parser = new MultipartParser();

parser.initWithBoundary(boundary);
parser.on('data', ({ name, buffer, start, end }) => {
switch (name) {
case 'partBegin':
resp.parts[partIndex] = {
body: null,
headers: {},
};
data = Buffer.from('');
break;
case 'headerField':
headerName = buffer.slice(start, end).toString();
break;
case 'headerValue':
headerValue = buffer.slice(start, end).toString();
break;
case 'headerEnd':
resp.parts[partIndex].headers[headerName.toLowerCase()] = headerValue;
break;
case 'partData':
data = Buffer.concat([data, buffer.slice(start, end)]);
break;
case 'partEnd':
resp.parts[partIndex].body = data;
partIndex++;
break;
}
});

parser.write(payload);

return callback(null, resp);
})
.catch(callback);
}
4 changes: 4 additions & 0 deletions test/_socketStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ module.exports = function createSocketStream(file, length) {
socketStream.destroy = function() {
};

// axios calls this
socketStream.setKeepAlive = function() {
};

socketStream.req = httpReqStream;
socketStream.res = httpResStream;

Expand Down
14 changes: 9 additions & 5 deletions test/client-customHttp-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ it('should allow customization of httpClient and the wsdl file download should p
socketStream.destroy = function() {
};

// axios calls this
socketStream.setKeepAlive = function() {
};

//Custom httpClient
function MyHttpClient (options, socket){
httpClient.call(this,options);
this.agent = new CustomAgent(options, socket);
class MyHttpClient extends httpClient {
constructor(options, socket) {
super(options)
this.agent = new CustomAgent(options, socket);
}
}

util.inherits(MyHttpClient, httpClient);

MyHttpClient.prototype.request = function(rurl, data, callback, exheaders, exoptions) {
var self = this;
var options = self.buildRequest(rurl, data, exheaders, exoptions);
Expand Down
10 changes: 5 additions & 5 deletions test/client-customHttp-xsdinclude-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ it('should allow customization of httpClient, the wsdl file, and associated data
};

//Custom httpClient
function MyHttpClient (options, wsdlSocket, xsdSocket){
httpClient.call(this,options);
this.agent = new CustomAgent(options, wsdlSocket, xsdSocket);
class MyHttpClient extends httpClient {
constructor(options, wsdlSocket, xsdSocket) {
super(options);
this.agent = new CustomAgent(options, wsdlSocket, xsdSocket);
}
}

util.inherits(MyHttpClient, httpClient);

MyHttpClient.prototype.request = function(rurl, data, callback, exheaders, exoptions) {
var self = this;
var options = self.buildRequest(rurl, data, exheaders, exoptions);
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"compileOnSave": true,
"compilerOptions": {
"target": "es3",
"target": "es2020",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with node 12 drop, the target can be bumped to ES2020 (based on https://node.green)

"module": "commonjs",
"moduleResolution": "nodenext",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed to have import transpiled to import and not back to require

"outDir": "lib",
"sourceMap": true,
"declaration": true,
Expand Down