Compare commits

...

50 Commits

Author SHA1 Message Date
3659b80e1e 1.1.46 2020-01-13 07:58:54 +00:00
770e7d46ea fix(core): update 2020-01-13 07:58:54 +00:00
2a46f2a306 1.1.45 2020-01-12 19:36:58 +00:00
eae4d09664 fix(core): update 2020-01-12 19:36:58 +00:00
23a2f597fc 1.1.44 2020-01-12 19:32:20 +00:00
c278249c32 fix(core): update 2020-01-12 19:32:20 +00:00
a32c372374 1.1.43 2019-11-21 17:32:31 +00:00
f98972d9fe fix(core): update 2019-11-21 17:32:30 +00:00
acebe6a381 1.1.42 2019-10-28 16:18:15 +01:00
7031504852 1.1.41 2019-10-27 14:41:49 +01:00
3010a1da9a fix(core): update 2019-10-27 14:41:48 +01:00
cdead33be4 1.1.40 2019-10-27 14:39:07 +01:00
5e23649702 fix(core): update 2019-10-27 14:39:06 +01:00
cc6bd5726a 1.1.39 2019-10-27 14:38:13 +01:00
f487584e80 fix(core): update 2019-10-27 14:38:12 +01:00
443bccd4c9 1.1.38 2019-10-27 14:36:18 +01:00
f359856b1d fix(core): update 2019-10-27 14:36:17 +01:00
bda04f124b 1.1.37 2019-10-27 14:32:27 +01:00
466187fd52 fix(core): update 2019-10-27 14:32:27 +01:00
d22504317e 1.1.36 2019-09-29 16:42:56 +02:00
6e31d84798 fix(core): update 2019-09-29 16:42:56 +02:00
36472b7306 1.1.35 2019-09-29 00:56:56 +02:00
e86f14b8d8 fix(core): update 2019-09-29 00:56:56 +02:00
2b9e7f6dd2 1.1.34 2019-09-29 00:43:37 +02:00
5e4afccf9c fix(core): update 2019-09-29 00:43:37 +02:00
3de7a1a210 1.1.33 2019-09-29 00:42:51 +02:00
bd2a5eedff fix(core): update 2019-09-29 00:42:51 +02:00
aa18357d75 1.1.32 2019-09-28 22:50:35 +02:00
9960aff219 fix(core): update 2019-09-28 22:50:35 +02:00
03d884ed59 1.1.31 2019-09-28 22:27:10 +02:00
9a0ac6fc62 fix(core): update 2019-09-28 22:27:09 +02:00
ad35ea4eb8 1.1.30 2019-09-28 21:40:05 +02:00
ffb0195f04 fix(core): update 2019-09-28 21:40:04 +02:00
78737c24df 1.1.29 2019-09-28 21:34:30 +02:00
6e276eda00 1.1.28 2019-09-28 21:33:14 +02:00
021d26a23a fix(core): update 2019-09-28 21:33:13 +02:00
c9c8a1234c 1.1.27 2019-09-08 18:00:49 +02:00
dffabd905f fix(core): update 2019-09-08 18:00:48 +02:00
36f2707141 1.1.26 2019-09-08 17:57:28 +02:00
b00d674b6f fix(core): update 2019-09-08 17:57:28 +02:00
b09598d465 1.1.25 2019-09-08 17:49:33 +02:00
acc7b2d46f fix(core): update 2019-09-08 17:49:32 +02:00
16a97a420c 1.1.24 2019-09-08 17:47:31 +02:00
a73c78e54b fix(core): update 2019-09-08 17:47:30 +02:00
1f408b5123 1.1.23 2019-08-22 12:40:19 +02:00
284f4967f4 fix(core): update 2019-08-22 12:40:19 +02:00
55c80c1403 1.1.22 2019-08-22 12:38:55 +02:00
7a3e565dbb fix(core): update 2019-08-22 12:38:55 +02:00
6f5d10ccd3 1.1.21 2019-08-22 12:38:12 +02:00
f1ddab72f6 fix(core): update 2019-08-22 12:38:11 +02:00
10 changed files with 684 additions and 406 deletions

View File

@ -3,14 +3,14 @@ image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: "$CI_BUILD_STAGE"
- .npmci_cache/
key: '$CI_BUILD_STAGE'
stages:
- security
- test
- release
- metadata
- security
- test
- release
- metadata
# ====================
# security stage
@ -18,60 +18,65 @@ stages:
mirror:
stage: security
script:
- npmci git mirror
- npmci git mirror
tags:
- docker
- notpriv
- lossless
- docker
- notpriv
snyk:
image: registry.gitlab.com/hosttoday/ht-docker-node:snyk
stage: security
script:
- npmci npm prepare
- npmci command npm install -g snyk
- npmci command npm install --ignore-scripts
- npmci command snyk test
tags:
- docker
- notpriv
- lossless
- docker
- notpriv
# ====================
# test stage
# ====================
testLTS:
testStable:
stage: test
script:
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci npm test
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- priv
- lossless
- docker
- priv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci command npm run build
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
- lossless
- docker
- notpriv
release:
stage: release
script:
- npmci node install lts
- npmci npm publish
- npmci node install stable
- npmci npm publish
only:
- tags
- tags
tags:
- docker
- notpriv
- lossless
- docker
- notpriv
# ====================
# metadata stage
@ -81,33 +86,35 @@ codequality:
allow_failure: true
script:
- npmci command npm install -g tslint typescript
- npmci npm prepare
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- docker
- priv
- lossless
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
- npmci trigger
only:
- tags
- tags
tags:
- docker
- notpriv
- lossless
- docker
- notpriv
pages:
image: hosttoday/ht-docker-dbase:npmci
services:
- docker:18-dind
stage: metadata
script:
- npmci node install lts
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command tsdoc
tags:
- lossless
- docker
- notpriv
only:
@ -115,5 +122,5 @@ pages:
artifacts:
expire_in: 1 week
paths:
- public
- public
allow_failure: true

751
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@pushrocks/smartrequest",
"version": "1.1.20",
"version": "1.1.46",
"private": false,
"description": "dropin replacement for request",
"main": "dist/index.js",
@ -23,24 +23,27 @@
},
"homepage": "https://gitlab.com/pushrocks/smartrequest#README",
"dependencies": {
"@pushrocks/smartpromise": "^3.0.2",
"@types/form-data": "^2.2.1",
"form-data": "^2.3.3"
"@pushrocks/smartpromise": "^3.0.5",
"@types/form-data": "^2.5.0",
"agentkeepalive": "^4.0.2",
"form-data": "^2.5.1"
},
"devDependencies": {
"@gitzone/tsbuild": "^2.1.8",
"@gitzone/tsrun": "^1.2.5",
"@gitzone/tstest": "^1.0.20",
"@pushrocks/tapbundle": "^3.0.9",
"@types/node": "^11.13.6"
"@gitzone/tsbuild": "^2.1.17",
"@gitzone/tsrun": "^1.2.8",
"@gitzone/tstest": "^1.0.24",
"@pushrocks/tapbundle": "^3.0.13",
"@types/node": "^12.7.8",
"tslint": "^5.20.0",
"tslint-config-prettier": "^1.18.0"
},
"files": [
"ts/*",
"ts_web/*",
"dist/*",
"dist_web/*",
"dist_ts_web/*",
"assets/*",
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_web/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"

View File

@ -20,7 +20,22 @@ dropin replacement for request
Use TypeScript for best in class instellisense.
> note: smartrequest uses the **native** node request module under the hood (not the one from npm)
### Features
- supports http
- supports https
- supports unix socks
- supports formData
- supports file uploads
- supports best practice keepAlive
- dedicated functions for working with JSON request/response cycles
- written in TypeScript
- continuously updated
- uses node native http and https modules
- used in modules like @pushrocks/smartproxy and @apiglobal/typedrequest
- commercial support available at [https://lossless.support](https://lossless.support)
> note: smartrequest uses the **native** node http/https modules under the hood (not the bloated one from npm)
```javascript
import * as smartrequest from 'smartrequest'
@ -31,23 +46,24 @@ let options: smartrequest.ISmartRequestOptions = { // typed options
"Content-Type": "application/json"
"Authorization": "Bearer token"
},
requestBody: {
requestBody: JSON.stringify({
key1: 'value1',
key2: 3
}
})
}
smartrequest.post('https://example.com', options).then(res => {
smartrequest.request('https://example.com', options).then(res => {
console.log(res.status)
console.log(res.body) // if json, body will be parsed automatically
}).catch(err => {
console.log(err)
})
// also available
smartrequest.get(...)
smartrequest.put(...)
smartrequest.del(...)
// dedicated JSON metods are available:
smartrequest.getJson(...)
smartrequest.postJson(...)
smartrequest.putJson(...)
smartrequest.delJson(...)
// streaming
smartrequest.get('https://example.com/bigfile.mp4', optionsArg, true).then(res => { // third arg = true signals streaming
@ -61,6 +77,10 @@ smartrequest.get('https://example.com/bigfile.mp4', optionsArg, true).then(res =
})
```
## Contribution
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)

View File

@ -6,6 +6,12 @@ tap.test('should request a html document over https', async () => {
await expect(smartrequest.getJson('https://encrypted.google.com/'))
.to.eventually.property('body')
.be.a('string');
await expect(smartrequest.getJson('https://encrypted.google.com/'))
.to.eventually.property('body')
.be.a('string');
await expect(smartrequest.getJson('https://encrypted.google.com/'))
.to.eventually.property('body')
.be.a('string');
});
tap.test('should request a JSON document over https', async () => {
@ -15,7 +21,7 @@ tap.test('should request a JSON document over https', async () => {
.equal(1);
});
tap.test('should post a JSON document over http', async () => {
tap.skip.test('should post a JSON document over http', async () => {
await expect(smartrequest.postJson('http://md5.jsontest.com/?text=example_text'))
.to.eventually.property('body')
.property('md5')

View File

@ -21,7 +21,7 @@ export const getBinary = async (
//so Buffer.concat() can make us a new Buffer
//of all of them together
const buffer = Buffer.concat(data);
response.body = buffer.toString('binary');
response.body = buffer;
done.resolve();
});
await done.promise;

View File

@ -8,16 +8,38 @@ import { request } from './smartrequest.request';
export interface IFormField {
name: string;
type: 'string' | 'filePath' | 'Buffer';
payload: string;
payload: string | Buffer;
fileName?: string;
contentType?: string;
}
const appendFormField = async (formDataArg: plugins.formData, formDataField: IFormField) => {
if (formDataField.type === 'filePath') {
let fileData = plugins.fs.readFileSync(plugins.path.join(process.cwd(), formDataField.payload));
formDataArg.append('file', fileData, {
filename: 'upload.pdf',
contentType: 'application/pdf'
});
switch (formDataField.type) {
case 'string':
formDataArg.append(formDataField.name, formDataField.payload);
break;
case 'filePath':
if (typeof formDataField.payload !== 'string') {
throw new Error(
`Payload for key ${
formDataField.name
} must be of type string. Got ${typeof formDataField.payload} instead.`
);
}
const fileData = plugins.fs.readFileSync(
plugins.path.join(process.cwd(), formDataField.payload)
);
formDataArg.append('file', fileData, {
filename: formDataField.fileName ? formDataField.fileName : 'upload.pdf',
contentType: 'application/pdf'
});
break;
case 'Buffer':
formDataArg.append(formDataField.name, formDataField.payload, {
filename: formDataField.fileName ? formDataField.fileName : 'upload.pdf',
contentType: formDataField.contentType ? formDataField.contentType : 'application/pdf'
});
break;
}
};
@ -30,14 +52,15 @@ export const postFormData = async (
for (const formField of payloadArg) {
await appendFormField(form, formField);
}
const requestOptions = Object.assign({}, optionsArg, {
const requestOptions = {
...optionsArg,
method: 'POST',
headers: {
...optionsArg.headers,
...form.getHeaders()
},
requestBody: form
});
};
// lets fire the actual request for sending the formdata
const response = await request(urlArg, requestOptions);

View File

@ -2,6 +2,7 @@ import * as plugins from './smartrequest.plugins';
import * as https from 'https';
export interface ISmartRequestOptions extends https.RequestOptions {
keepAlive?: boolean;
requestBody?: any;
autoJsonParse?: boolean;
}

View File

@ -1,4 +1,4 @@
import * as formData from 'form-data';
import formData from 'form-data';
import * as fs from 'fs';
import * as http from 'http';
import * as https from 'https';
@ -8,3 +8,8 @@ import * as url from 'url';
import * as smartpromise from '@pushrocks/smartpromise';
export { formData, http, https, fs, path, url, smartpromise };
// third party scope
import * as agentkeepalive from 'agentkeepalive';
export { agentkeepalive };

View File

@ -1,4 +1,3 @@
import * as https from 'https';
import * as plugins from './smartrequest.plugins';
import * as interfaces from './smartrequest.interfaces';
@ -12,14 +11,14 @@ const buildUtf8Response = (
incomingMessageArg: IncomingMessage,
autoJsonParse = true
): Promise<IExtendedIncomingMessage> => {
let done = plugins.smartpromise.defer<IExtendedIncomingMessage>();
const done = plugins.smartpromise.defer<IExtendedIncomingMessage>();
// Continuously update stream with data
let body = '';
incomingMessageArg.on('data', function(chunkArg) {
incomingMessageArg.on('data', chunkArg => {
body += chunkArg;
});
incomingMessageArg.on('end', function() {
incomingMessageArg.on('end', () => {
if (autoJsonParse) {
try {
(incomingMessageArg as IExtendedIncomingMessage).body = JSON.parse(body);
@ -55,27 +54,47 @@ const parseSocketPathAndRoute = (stringToParseArg: string) => {
};
};
const httpAgent = new plugins.http.Agent({
keepAlive: true,
keepAliveMsecs: 600000
/**
* a custom http agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpAgent = new plugins.agentkeepalive.default();
/**
* a custom http agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpAgentKeepAliveFalse = new plugins.http.Agent({
maxFreeSockets: 0,
keepAlive: false,
keepAliveMsecs: 0
});
const httpsAgent = new plugins.https.Agent({
keepAlive: true,
keepAliveMsecs: 600000
/**
* a custom https agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpsAgent = new plugins.agentkeepalive.HttpsAgent();
/**
* a custom https agent to make sure we can set custom keepAlive options for speedy subsequent calls
*/
const httpsAgentKeepAliveFalse = new plugins.https.Agent({
maxFreeSockets: 0,
keepAlive: false,
keepAliveMsecs: 0
});
export let request = async (
domainArg: string,
optionsArg: interfaces.ISmartRequestOptions = {},
streamArg: boolean = false
responseStreamArg: boolean = false,
requestDataFunc: (req: plugins.http.ClientRequest) => void = null
): Promise<IExtendedIncomingMessage> => {
let done = plugins.smartpromise.defer<any>();
const done = plugins.smartpromise.defer<any>();
// merge options
const defaultOptions: interfaces.ISmartRequestOptions = {
// agent: agent,
autoJsonParse: true
autoJsonParse: true,
keepAlive: true
};
optionsArg = {
@ -84,11 +103,10 @@ export let request = async (
};
// parse url
let parsedUrl: plugins.url.Url;
parsedUrl = plugins.url.parse(domainArg);
const parsedUrl = plugins.url.parse(domainArg);
optionsArg.hostname = parsedUrl.hostname;
if (parsedUrl.port) {
optionsArg.port = parseInt(parsedUrl.port);
optionsArg.port = parseInt(parsedUrl.port, 10);
}
optionsArg.path = parsedUrl.path;
@ -103,45 +121,25 @@ export let request = async (
// lets determine the request module to use
const requestModule = (() => {
if (parsedUrl.protocol === 'https:') {
optionsArg.agent = httpsAgent;
return plugins.https;
} else if (parsedUrl.protocol === 'http:') {
optionsArg.agent = httpAgent;
return plugins.http;
} else {
throw new Error(`unsupported protocol: ${parsedUrl.protocol}`);
switch (true) {
case parsedUrl.protocol === 'https:' && optionsArg.keepAlive:
optionsArg.agent = httpsAgent;
return plugins.https;
case parsedUrl.protocol === 'https:' && !optionsArg.keepAlive:
optionsArg.agent = httpsAgentKeepAliveFalse;
return plugins.https;
case parsedUrl.protocol === 'http:' && optionsArg.keepAlive:
optionsArg.agent = httpAgent;
return plugins.http;
case parsedUrl.protocol === 'http:' && !optionsArg.keepAlive:
optionsArg.agent = httpAgentKeepAliveFalse;
return plugins.http;
}
})() as typeof plugins.https;
// lets perform the actual request
let request = requestModule.request(optionsArg);
// lets write the requestBody
if (optionsArg.requestBody) {
if (!(optionsArg.requestBody instanceof plugins.formData)) {
if (typeof optionsArg.requestBody !== 'string') {
optionsArg.requestBody = JSON.stringify(optionsArg.requestBody);
}
request.write(optionsArg.requestBody);
request.end();
} else if (optionsArg.requestBody instanceof plugins.formData) {
optionsArg.requestBody.pipe(request).on('finish', event => {
request.end();
});
}
} else {
request.end();
}
// lets handle an error
request.on('error', e => {
console.error(e);
});
// lets handle the response
request.on('response', async response => {
if (streamArg) {
const requestToFire = requestModule.request(optionsArg, async response => {
if (responseStreamArg) {
done.resolve(response);
} else {
const builtResponse = await buildUtf8Response(response, optionsArg.autoJsonParse);
@ -149,6 +147,30 @@ export let request = async (
}
});
// lets write the requestBody
if (optionsArg.requestBody) {
if (optionsArg.requestBody instanceof plugins.formData) {
optionsArg.requestBody.pipe(requestToFire).on('finish', event => {
requestToFire.end();
});
} else {
if (typeof optionsArg.requestBody !== 'string') {
optionsArg.requestBody = JSON.stringify(optionsArg.requestBody);
}
requestToFire.write(optionsArg.requestBody);
requestToFire.end();
}
} else if (requestDataFunc) {
requestDataFunc(requestToFire);
} else {
requestToFire.end();
}
// lets handle an error
requestToFire.on('error', e => {
console.error(e);
});
const result = await done.promise;
return result;
};