A minimal node SOAP client and server implementation for Node.js applications
—
HTTP transport layer with support for custom agents, proxies, SSL configuration, MTOM attachments, and comprehensive request/response handling for SOAP communication.
Core HTTP transport implementation providing request/response handling with extensive configuration options.
/**
* HTTP client for SOAP transport layer
* Handles all HTTP communication between SOAP client and server
*/
class HttpClient implements IHttpClient {
/** HTTP request options */
options: IOptions;
constructor(options?: IOptions);
/**
* Build the HTTP request (method, uri, headers, ...)
* @param rurl - The resource url
* @param data - The payload
* @param exheaders - Extra http headers
* @param exoptions - Extra options
* @returns The http request object for the request module
*/
buildRequest(rurl: string, data: any, exheaders?: IHeaders, exoptions?: IExOptions): any;
/**
* Handle the http response
* @param req - The request object
* @param res - The response object
* @param body - The http body
* @returns The parsed body
*/
handleResponse(req: any, res: any, body: any): any;
/**
* Make HTTP request to SOAP endpoint
* @param rurl - Target URL
* @param data - Request body data
* @param callback - Callback receiving (error, response, body)
* @param exheaders - HTTP headers to include
* @param exoptions - Additional request options
* @param caller - Calling context
*/
request(
rurl: string,
data: any,
callback: (error: any, res?: any, body?: any) => any,
exheaders?: IHeaders,
exoptions?: IExOptions,
caller?: any
): any;
/**
* Stream-based HTTP request (optional)
* @param rurl - Target URL
* @param data - Request body data
* @param exheaders - HTTP headers
* @param exoptions - Request options
* @param caller - Calling context
* @returns Readable stream
*/
requestStream?(
rurl: string,
data: any,
exheaders?: IHeaders,
exoptions?: IExOptions,
caller?: any
): any;
}
/**
* HTTP client interface for custom implementations
*/
interface IHttpClient {
request(rurl: string, data: any, callback: (error: any, res?: any, body?: any) => any, exheaders?: IHeaders, exoptions?: IExOptions, caller?: any): any;
requestStream?(rurl: string, data: any, exheaders?: IHeaders, exoptions?: IExOptions, caller?: any): any;
}Usage Examples:
import { createClientAsync, HttpClient } from "soap";
// Use default HTTP client
const client = await createClientAsync("http://example.com/service.wsdl");
// Create custom HTTP client
const customHttpClient = new HttpClient({
timeout: 30000,
proxy: 'http://proxy.company.com:8080'
});
const clientWithCustomHttp = await createClientAsync(
"http://example.com/service.wsdl",
{ httpClient: customHttpClient }
);Comprehensive configuration options for HTTP transport behavior.
interface IHttpClientOptions {
/** Request timeout in milliseconds */
timeout?: number;
/** Proxy server URL */
proxy?: string;
/** Custom HTTP headers for all requests */
headers?: IHeaders;
/** HTTPS agent configuration */
httpsAgent?: any;
/** HTTP agent configuration */
httpAgent?: any;
/** Maximum number of redirects to follow */
maxRedirects?: number;
/** SSL/TLS options */
ssl?: {
/** Private key for client certificate */
key?: string | Buffer;
/** Client certificate */
cert?: string | Buffer;
/** Certificate Authority certificates */
ca?: string | Buffer | Array<string | Buffer>;
/** PFX certificate data */
pfx?: string | Buffer;
/** Passphrase for certificate */
passphrase?: string;
/** Reject unauthorized certificates */
rejectUnauthorized?: boolean;
};
/** Enable request/response logging */
enableLogging?: boolean;
/** Custom user agent string */
userAgent?: string;
}Usage Examples:
// HTTP client with SSL configuration
const sslHttpClient = new HttpClient({
timeout: 60000,
ssl: {
key: fs.readFileSync('client-key.pem'),
cert: fs.readFileSync('client-cert.pem'),
ca: fs.readFileSync('ca-cert.pem'),
rejectUnauthorized: true
}
});
// HTTP client with proxy
const proxyHttpClient = new HttpClient({
proxy: 'http://username:password@proxy.company.com:8080',
headers: {
'User-Agent': 'MySOAPClient/1.0'
}
});Support for MTOM attachments in SOAP messages for efficient binary data transfer.
/**
* Parse MTOM response containing binary attachments
* @param payload - Raw MTOM response buffer
* @param boundary - MIME boundary string from Content-Type header
* @param callback - Callback receiving (error, parsedResponse)
* @returns Promise that resolves when parsing is complete
*/
function parseMTOMResp(
payload: Buffer,
boundary: string,
callback: (err?: Error, resp?: IMTOMAttachments) => void
): Promise<void>;
/**
* MTOM response structure with attachments
*/
interface IMTOMResponse {
/** Main SOAP response body */
body: any;
/** Array of binary attachments */
attachments: IMTOMAttachment[];
}
/**
* Individual MTOM attachment
*/
interface IMTOMAttachment {
/** Attachment content-type */
contentType: string;
/** Attachment content-id */
contentId: string;
/** Binary attachment data */
body: Buffer;
/** Attachment headers */
headers: IHeaders;
}Usage Examples:
import { parseMTOMResp } from "soap";
// Handle MTOM response in custom HTTP client
class MTOMHttpClient implements IHttpClient {
request(url: any, data: any, callback: Function, headers?: any, options?: any) {
// Make HTTP request...
// Check if response is MTOM
const contentType = response.headers['content-type'];
if (contentType && contentType.includes('multipart/related')) {
const boundary = contentType.match(/boundary=([^;]+)/)?.[1];
parseMTOMResp(response.body, boundary, (err, parsed) => {
if (err) return callback(err);
console.log('Main response:', parsed.body);
console.log('Attachments:', parsed.attachments.length);
callback(null, response, parsed.body);
});
} else {
callback(null, response, response.body);
}
}
}Intercept and modify HTTP requests and responses for custom processing.
/**
* Custom HTTP client with request/response interceptors
*/
class InterceptingHttpClient implements IHttpClient {
constructor(
private requestInterceptor?: (options: any) => any,
private responseInterceptor?: (response: any) => any
) {}
request(url: any, data: any, callback: Function, headers?: any, options?: any) {
// Apply request interceptor
if (this.requestInterceptor) {
const requestOptions = { url, data, headers, ...options };
const modifiedOptions = this.requestInterceptor(requestOptions);
({ url, data, headers, ...options } = modifiedOptions);
}
// Make actual HTTP request...
// Apply response interceptor
if (this.responseInterceptor && response) {
response = this.responseInterceptor(response);
}
callback(error, response, body);
}
}Usage Examples:
// Create HTTP client with logging interceptors
const loggingHttpClient = new InterceptingHttpClient(
// Request interceptor
(options) => {
console.log('Outgoing request:', {
url: options.url,
headers: options.headers,
bodySize: options.data?.length
});
return options;
},
// Response interceptor
(response) => {
console.log('Incoming response:', {
status: response.statusCode,
headers: response.headers,
bodySize: response.body?.length
});
return response;
}
);
const client = await createClientAsync("http://example.com/service.wsdl", {
httpClient: loggingHttpClient
});Configure custom HTTP/HTTPS agents for connection pooling and advanced networking options.
/**
* HTTP agent configuration for connection management
*/
interface IAgentOptions {
/** Keep connections alive */
keepAlive?: boolean;
/** Keep alive initial delay */
keepAliveInitialDelay?: number;
/** Maximum number of sockets per host */
maxSockets?: number;
/** Maximum number of free sockets per host */
maxFreeSockets?: number;
/** Timeout for socket connections */
timeout?: number;
/** Free socket timeout */
freeSocketTimeout?: number;
}Usage Examples:
import * as https from 'https';
import * as http from 'http';
// Custom HTTPS agent with connection pooling
const httpsAgent = new https.Agent({
keepAlive: true,
maxSockets: 10,
timeout: 30000,
rejectUnauthorized: false
});
const httpClient = new HttpClient({
httpsAgent: httpsAgent,
timeout: 60000
});
// Custom HTTP agent for non-SSL connections
const httpAgent = new http.Agent({
keepAlive: true,
maxSockets: 5
});
const httpClientWithAgent = new HttpClient({
httpAgent: httpAgent,
httpsAgent: httpsAgent
});Utility functions for XML processing, namespace handling, and data escaping.
/**
* Escape XML entities in object values
* @param obj - Object containing values to escape
* @returns Object with XML-escaped string values
*/
function xmlEscape(obj: any): any;
/**
* Find namespace prefix for given URI
* @param xmlnsMapping - Namespace mapping object
* @param nsURI - Namespace URI to find prefix for
* @returns Namespace prefix or empty string
*/
function findPrefix(xmlnsMapping: any, nsURI: any): string;
/**
* Split qualified name into prefix and local name
* @param nsName - Qualified name (e.g., "ns1:ElementName")
* @returns Object with prefix and name properties
*/
function splitQName<T>(nsName: T): { prefix: string; name: T | string };Usage Examples:
import { xmlEscape, findPrefix, splitQName } from "soap";
// XML escaping
const userInput = {
name: "John & Jane's Company",
description: '<script>alert("xss")</script>'
};
const escaped = xmlEscape(userInput);
console.log(escaped);
// {
// name: "John & Jane's Company",
// description: "<script>alert("xss")</script>"
// }
// Use in SOAP call
const result = await client.CreateUserAsync(escaped);
// Namespace utilities
const nsMapping = {
'ns1': 'http://example.com/service',
'ns2': 'http://example.com/types'
};
const prefix = findPrefix(nsMapping, 'http://example.com/service');
console.log(prefix); // 'ns1'
// Split qualified name
const qname = splitQName('ns1:GetUserInfo');
console.log(qname); // { prefix: 'ns1', name: 'GetUserInfo' }Optimize performance with HTTP connection pooling for high-throughput scenarios.
import * as https from 'https';
// Configure agent with connection pooling
const poolingAgent = new https.Agent({
keepAlive: true,
keepAliveInitialDelay: 1000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 30000,
freeSocketTimeout: 15000
});
const httpClient = new HttpClient({
httpsAgent: poolingAgent
});Implement request retry logic for handling transient network failures.
class RetryHttpClient implements IHttpClient {
constructor(
private maxRetries: number = 3,
private retryDelay: number = 1000
) {}
request(url: any, data: any, callback: Function, headers?: any, options?: any) {
let attempts = 0;
const attemptRequest = () => {
attempts++;
// Make HTTP request using underlying implementation
this.makeRequest(url, data, (error, response, body) => {
if (error && attempts < this.maxRetries) {
setTimeout(attemptRequest, this.retryDelay * attempts);
return;
}
callback(error, response, body);
}, headers, options);
};
attemptRequest();
}
private makeRequest(url: any, data: any, callback: Function, headers?: any, options?: any) {
// Actual HTTP request implementation
}
}Handle compressed responses for improved performance over slow networks.
const httpClient = new HttpClient({
headers: {
'Accept-Encoding': 'gzip, deflate'
}
});Install with Tessl CLI
npx tessl i tessl/npm-soap