Skip to content

Commit 7e19956

Browse files
Refactored README.md; fixed callbacks cleaning;
1 parent ec06e19 commit 7e19956

File tree

8 files changed

+115
-91
lines changed

8 files changed

+115
-91
lines changed

.npmignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
/public/
55
/test/
66
/src/
7-
/dist/safe-jsonp.spec.js
8-
gulpfile.js
7+
gulpfile.js
8+
.travis.yml

README.md

Lines changed: 87 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@
77

88
A sandboxed JSONP implementation for the browser.
99

10+
# Why
11+
12+
If for any reason you still have to use JSONP instead of ajax & CORS on a page with sensitive data to fetch data
13+
from third-party services, you can use this package which makes jsonp requests more secure using a temporary sandboxed
14+
iframe as a proxy. This could potentially protect from the XSS attack injected in the jsonp response,
15+
since the sandbox iframe does not have unsafe access to its parent page. At the same time, json data can be sent
16+
from iframe to the parent document as a simple json string using the window.postMessage feature.
17+
The package supports sandbox and non-sandbox mode (like typical jsonp packages), by default
18+
sandbox mode is preffered, but not required.
19+
1020
# Features
11-
- optional sandbox mechanism for safer requests to untrusted origins (internally used iframes)
12-
- support Promise and callback style
21+
- **optional sandbox mechanism for safer requests to untrusted origins (internally used iframes)**
22+
- support Promise and callback styles
1323
- support custom Promise class
1424
- anti-caching `_rnd` query param
1525
- support query params in url string and/or options.params property
1626
- automatically encoding params, converting objects and arrays params to JSON strings
1727

18-
## Functional diagram
19-
Sandbox mode:
20-
21-
![Sandbox functional diagram](https://github.com/DigitalBrainJS/safe-jsonp/raw/master/public/safe-jsonp.png)
22-
2328
## Try It!
24-
[Github demo](http://htmlpreview.github.io/?https://github.com/DigitalBrainJS/safe-jsonp/blob/master/public/index.html)
25-
26-
[JSFiddle.net demo](https://jsfiddle.net/DigitalBrain/ugz5qn0r/10/)
29+
[JSFiddle.net demo](https://jsfiddle.net/DigitalBrain/ugz5qn0r/)
2730

2831
## Installation
2932

@@ -33,7 +36,31 @@ Install for node.js or browserify using `npm`:
3336
$ npm install safe-jsonp --save
3437
```
3538

36-
## Direct usage in the browser
39+
## Basic usage example
40+
Promise style:
41+
```javascript
42+
import JSONP from "safe-jsonp";
43+
44+
JSONP('http://api.github.com/users/DigitalBrainJS')
45+
.then( data => console.log('JSONP data object:', data))
46+
.catch( err => console.warn('Oops...we got an error', err.message))
47+
```
48+
49+
50+
Callback style:
51+
```javascript
52+
import JSONP from "safe-jsonp";
53+
54+
JSONP('http://api.github.com/users/DigitalBrainJS', (err, data) => {
55+
if(err){
56+
console.warn('Oops...we got an error', err.message)
57+
}else{
58+
console.log('JSON data:', data)
59+
}
60+
})
61+
```
62+
63+
## CDN
3764
Use unpkg.com cdn to get link to script/module from the package:
3865
- UMD ES5 version (~15kB)
3966
```html
@@ -44,105 +71,54 @@ Use unpkg.com cdn to get link to script/module from the package:
4471
<script src="https://unpkg.com/safe-jsonp/dist/safe-jsonp.umd.min.js"></script>
4572
```
4673
- ESM ES2015 module (~14kB)
47-
```html
74+
```javascript
4875
import JSONP from "https://unpkg.com/safe-jsonp/dist/safe-jsonp.esm.js"
4976
```
50-
- minified ESM ES2015 module (~7kB)
51-
```html
52-
import JSONP from "https://unpkg.com/safe-jsonp/dist/safe-jsonp.esm.min.js"
53-
```
54-
55-
56-
## API
57-
58-
### JSONP(url: String, [options: Object]): \<Promise>
59-
### JSONP(url: String, [options: Object], cb: Function): \<JSONP>
60-
61-
- `url: String` url to fetch
62-
- `[options: Object]`
63-
- `sandbox: Boolean|Undefined= undefined` sets sandbox mode for query handling to untrusted origins.
64-
Default `undefined` value means prefer sandboxed mode, but allow non-sandboxed query if the environment doesn't
65-
support it. In sandboxed mode all requests will be done in invisible iframe proxy, created temporally for each
66-
origin
67-
- `idleTimeout: Number= 15000` idle timeout for each sandbox in ms
68-
- `params: Object` Object with query params to combine with a URL string
69-
- `timeout: Number= 15000` max query pending time in ms. Default: `15000` (15 seconds)
70-
- `preventCache: Boolean= true` force disable cache by adding timestamp to a query param `_rnd`
71-
- `cbParam: String= 'callback'` name of the query param used by backend to get the name of the JSONP callback
72-
- `Promise: Function` Promise class that be used instead of native (if environment supports it)
73-
- `abortable: Boolean` enables ability to abort for Promise mode. If this option is set to true,
74-
an additional property called abort will be created in options object.
75-
This allows to get the abort function via shared options object.
76-
- `[cb: Function(err: ?Error, [data: Object])]` callback function, called when jsonp query is complete
77-
(with success or error).
78-
If this argument is omitted, the function returns a Promise, otherwise, a JSONP instance will be returned.
79-
80-
Returns a promise or JSON instance depending on the presence of a callback argument
81-
82-
### JSONP class instance
83-
*instance methods:*
84-
- `abort()` aborts the jsonp query with `Error: aborted`, handled by callback or Promise chain.
77+
## Functional diagram
78+
Sandbox mode:
8579

86-
*static methods:*
87-
- `parseURL(url: String): URL|Object` parse URL into components
88-
- `parseParams(url: String): Object` parse URL params string eg. `a=1&b=2` to params object `{a:1, b:2}`
89-
- `encodeParams(params: Object): String` encode params object to string
90-
91-
## Usage example
92-
Promise style:
93-
```javascript
94-
JSONP('http://api.github.com/users/DigitalBrainJS')
95-
.then( data => console.log('JSONP data object:', data), err => console.warn('Oops...we got an error', err.message))
96-
```
80+
![Sandbox functional diagram](https://github.com/DigitalBrainJS/safe-jsonp/raw/master/public/safe-jsonp.png)
9781

82+
## More examples
83+
##### additional options:
9884
```javascript
99-
//in the context of the ES2015 async function
100-
10185
const Promise = require("bluebird");
10286

87+
//...async function
88+
10389
const data= await JSONP('http://api.github.com/users/DigitalBrainJS?name=bla&age=23', {
10490
params: {
10591
foo: 1,
106-
bar: [1,2,3]// We can pass objects and arrays as a param value
92+
bar: [1,2,3] // We can pass objects and arrays as a param value
10793
},
10894

10995
timeout: 60000, //60 seconds
11096
preventCache: true,
11197
cbParam: 'callback',
112-
Promise
98+
Promise //custom Promise class
11399
})
114100

115101
//will make request like https://api.github.com/users/DigitalBrainJS?name=bla&age=23&foo=1&bar=%5B1%2C2%2C3%5D&callback=_jsonpvqz.cb0
116102
//callback param is randomly generated to avoid collisions
117103
```
118104

119-
Callback style:
120-
```javascript
121-
JSONP('http://api.github.com/users/DigitalBrainJS', (err, data) => {
122-
if(err){
123-
console.warn('Oops...we got an error', err.message)
124-
}else{
125-
console.log('JSON data:', data)
126-
}
127-
})
128-
```
129105

130-
Accept sandbox mode only
106+
##### Force sandbox mode:
131107
```javascript
132108
JSONP('http://api.github.com/users/DigitalBrainJS', {sandbox: true})
133109
.then(data=>console.log(data), err=>console.warn(err))
134-
110+
//will fail if the browser doesn't support sandbox mode or data/blob uri for iframe
135111
```
136112

137-
Aborting the request:
113+
##### Aborting the request:
138114
```javascript
139115
const jsonp= JSONP('http://api.github.com/users/DigitalBrainJS', (err, data) => {
140116
console.log(err) //Error: aborted
141117
});
142118

143119
jsonp.abort();
144120
```
145-
Or
121+
Or when using Promise:
146122
```javascript
147123
const sharedOptions= {abortable: true};
148124

@@ -152,6 +128,41 @@ JSONP('http://api.github.com/users/DigitalBrainJS', sharedOptions)
152128
sharedOptions.abort();
153129
```
154130

131+
## API
132+
133+
### JSONP(url: String, [options: Object]): \<Promise>
134+
### JSONP(url: String, [options: Object], cb: Function): \<JSONP>
135+
136+
- `url: String` url to fetch
137+
- `[options: Object]`
138+
- `sandbox: Boolean|Undefined= undefined` sets sandbox mode for query handling to untrusted origins.
139+
Default `undefined` value means prefer sandboxed mode, but allow non-sandboxed query if the environment doesn't
140+
support it. In sandboxed mode all requests will be done in invisible iframe proxy, created temporally for each
141+
origin
142+
- `idleTimeout: Number= 15000` idle timeout for each sandbox in ms
143+
- `params: Object` Object with query params to combine with a URL string
144+
- `timeout: Number= 15000` max query pending time in ms. Default: `15000` (15 seconds)
145+
- `preventCache: Boolean= true` force disable cache by adding timestamp to a query param `_rnd`
146+
- `cbParam: String= 'callback'` name of the query param used by backend to get the name of the JSONP callback
147+
- `Promise: Function` Promise class that be used instead of native (if environment supports it)
148+
- `abortable: Boolean` enables ability to abort for Promise mode. If this option is set to true,
149+
an additional property called abort will be created in options object.
150+
This allows to get the abort function via shared options object.
151+
- `[cb: Function(err: ?Error, [data: Object])]` callback function, called when jsonp query is complete
152+
(with success or error).
153+
If this argument is omitted, the function returns a Promise, otherwise, a JSONP instance will be returned.
154+
155+
Returns a promise or JSON instance depending on the presence of a callback argument
156+
157+
### JSONP class instance
158+
*instance methods:*
159+
- `abort()` aborts the jsonp query with `Error: aborted`, handled by callback or Promise chain.
160+
161+
*static methods:*
162+
- `parseURL(url: String): URL|Object` parse URL into components
163+
- `parseParams(url: String): Object` parse URL params string eg. `a=1&b=2` to params object `{a:1, b:2}`
164+
- `encodeParams(params: Object): String` encode params object to string
165+
155166
## License
156167

157168
The MIT License (MIT)

esm.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {default} from "./dist/safe-jsonp.esm"

gulpfile.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ function createBuildTask(entryFile, buildOptions) {
3030
toES5 = false,
3131
outFile = `${name}.${format}${ext}`,
3232
taskTargetName = outFile,
33-
minify = false,
3433
include = "node_modules/**",
35-
exclude
34+
exclude,
35+
minify
3636
} = buildOptions || {};
3737

3838

@@ -72,7 +72,7 @@ function createBuildTask(entryFile, buildOptions) {
7272

7373
noSource: true
7474
}) : noop())
75-
.pipe(minify ? gulp.dest(destPath) : noop())
75+
.pipe(minify ? gulp.dest(destPath) : noop());
7676
});
7777

7878
return taskName;
@@ -91,11 +91,12 @@ gulp.task("webserver", function () {
9191
});
9292

9393
const clientBuildTask = createBuildTask(clientEntryFile, {exportName: "JSONP", toES5: true, minify: true});
94-
const clientBuildTaskES = createBuildTask(clientEntryFile, {format: "esm", minify: true});
94+
const clientBuildTaskES = createBuildTask(clientEntryFile, {format: "esm"});
9595
const clientBuildTests = createBuildTask("test/safe-jsonp.spec.js", {
9696
taskTargetName: "test",
9797
format: "cjs",
98-
toES5: true
98+
toES5: true,
99+
destPath: "./test/"
99100
});
100101

101102
gulp.task("build", function (done) {

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"main": "index.js",
77
"browser": "./dist/safe-jsonp.umd.js",
88
"module": "./dist/safe-jsonp.esm.js",
9+
"unpkg": "./dist/safe-jsonp.umd.min.js",
10+
"jsnext:main": "./dist/safe-jsonp.esm.js",
911
"scripts": {
1012
"test": "npm run build && mocha-phantomjs test/test.html",
1113
"build": "shx rm -rf dist && gulp build",
@@ -22,6 +24,11 @@
2224
"type": "git",
2325
"url": "https://github.com/DigitalBrainJS/safe-jsonp.git"
2426
},
27+
"files": [
28+
"index.js",
29+
"esm.js",
30+
"./dist/"
31+
],
2532
"keywords": [
2633
"jsonp",
2734
"json",
@@ -38,6 +45,11 @@
3845
"script",
3946
"cors"
4047
],
48+
"reflinks": [
49+
"jsonp",
50+
"json",
51+
"fetch"
52+
],
4153
"publishConfig": {
4254
"registry": "https://registry.npmjs.org"
4355
},

src/lib/fetch.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {encodeParams, randomStr, mixin} from "./utils";
1+
import {encodeParams, randomStr, mixin, generateUniquePropName} from "./utils";
22

33
export default function fetch(url, options, callback) {
44
let wasCalled, isComplete, script, timer;
@@ -20,6 +20,8 @@ export default function fetch(url, options, callback) {
2020
publicCallback = function (data) {
2121
const {length} = arguments;
2222

23+
delete register[cbName];
24+
2325
wasCalled = true;
2426
if (!length) {
2527
done("data argument is missing");
@@ -43,11 +45,7 @@ export default function fetch(url, options, callback) {
4345
cbParam = "callback";
4446
}
4547

46-
let cbName, i = 0;
47-
48-
while ((cbName = `cb${i.toString(36)}`) in register) {
49-
i++;
50-
}
48+
let cbName = "c" + generateUniquePropName(register, (i) => randomStr(i / 10 + 2));
5149

5250
register[cbName] = publicCallback;
5351

src/lib/sandbox.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export default function Sandbox(options) {
2727
proxy,
2828
mixin,
2929
encodeParams,
30-
randomStr
30+
randomStr,
31+
generateUniquePropName
3132
};
3233

3334
let iframe = document.createElement("iframe"),

test/test.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
</script>
2020
<script src="../dist/safe-jsonp.umd.js"></script>
2121
<!--<script src="safe-jsonp.spec.js"></script>-->
22-
<script src="../dist/safe-jsonp.spec.cjs.js"></script>
22+
<script src="../test/safe-jsonp.spec.cjs.js"></script>
2323
<script>
2424
mocha.run();
2525
</script>

0 commit comments

Comments
 (0)