Skip to content

Commit bada8b2

Browse files
authoredNov 6, 2021
Add: retry on failure (#273)
* Bump deps * Add: retry on failure * Use retry
1 parent 7c65642 commit bada8b2

12 files changed

+2061
-2030
lines changed
 

‎README.md

+21
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,27 @@ pbs.twimg.com/media/$1:orig
118118
* Lines starting with `#` are ignored.
119119
* Empty lines are ignored.
120120

121+
Retry on failure
122+
----------------
123+
124+
Some sites return an error when getting too much traffic. You can make the extension wait some time and try again by defining retry rules:
125+
126+
```
127+
# example
128+
example\.com/images/.*
129+
3 5 1
130+
```
131+
132+
* Each rule includes:
133+
- A line of regex matching the URL.
134+
- A line of numbers, representing `maxRetries`, `delay`, and `exponentialBackoff`.
135+
* `maxRetries` - the extension will retry `maxRetries` times before raising a network error.
136+
* `delay` - after an error occurs, wait `delay` seconds before the next try.
137+
* `exponentialBackoff` - increase the delay gradually. For example, when `delay` is `5` and `exponentialBackoff` is `2`, the first delay will be 5s, the second will be 10s, the third will be 20s, etc.
138+
* Lines starting with `#` are ignored.
139+
* Empty lines are ignored.
140+
141+
121142
Domain blacklist
122143
----------------
123144

‎package-lock.json

+1,945-2,016
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
"devDependencies": {
2525
"@eight04/idb-storage": "^0.4.2",
2626
"content-disposition": "^0.5.3",
27-
"eslint": "^7.32.0",
27+
"eslint": "^8.1.0",
2828
"espression": "^1.8.5",
29-
"mime": "^2.5.2",
29+
"mime": "^3.0.0",
3030
"mkdoc": "^1.0.49",
3131
"node-sigint": "0.0.1",
32-
"rollup": "^2.55.1",
32+
"rollup": "^2.59.0",
3333
"rollup-plugin-cjs-es": "^1.0.1",
3434
"rollup-plugin-copy": "^3.4.0",
3535
"rollup-plugin-iife": "^0.5.0",
@@ -42,7 +42,7 @@
4242
"stream-throttle": "^0.1.3",
4343
"sync-version": "^1.0.1",
4444
"tiny-glob": "^0.2.9",
45-
"web-ext": "^6.2.0",
45+
"web-ext": "^6.5.0",
4646
"webext-menus": "^0.3.2",
4747
"webext-pref": "^0.6.0",
4848
"webext-pref-ui": "^0.2.1",

‎src/lib/image-cache.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import browser from "webextension-polyfill";
44
import {IS_CHROME} from "./env.js";
55
import {fetchImage} from "./fetch-image.js";
66
import {fetchXHR} from "./fetch.js";
7+
import {retry} from "./retry.js";
78

89
export const imageCache = createImageCache();
910

@@ -26,7 +27,7 @@ function createImageCache() {
2627

2728
function add({url, tabId, frameId, referrer}) {
2829
return cache.set(url, async () => {
29-
const data = await _fetchImage(url, tabId, frameId, referrer);
30+
const data = await retry(() => _fetchImage(url, tabId, frameId, referrer), url);
3031
const resource = data.blob;
3132
delete data.blob;
3233
const meta = Object.assign(data, await detectDimension(resource));

‎src/lib/pref.js

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const DEFAULT = {
4040
srcAlternative: "data-src, data-gifsrc, gifsrc",
4141
previewMaxHeight: 200,
4242
previewMaxHeightUpperBound: 200,
43+
retryOnFailure: "",
4344
urlMap: "",
4445
useCache: true,
4546
useExpression: false,

‎src/lib/readline.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function *parseText(text, groupBy = 1) {
2+
const lines = text.split(/\r?\n/g).filter(line =>
3+
line && /\S/.test(line) && !line.startsWith("#"));
4+
for (let i = 0; i < lines.length; i += groupBy) {
5+
yield lines.slice(i, i + groupBy);
6+
}
7+
}

‎src/lib/retry.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {pref} from "./pref.js";
2+
import {parseText} from "./readline.js";
3+
import {timeout} from './timeout.js';
4+
5+
let rules = [];
6+
7+
pref.ready().then(() => {
8+
update();
9+
pref.on("change", change => {
10+
if (change.retryOnFailure != null) {
11+
update();
12+
}
13+
});
14+
});
15+
16+
function update() {
17+
rules = [...parseText(pref.get('retryOnFailure'), 2)]
18+
.map(createRule);
19+
}
20+
21+
function createRule(lines) {
22+
const rx = new RegExp(lines[0], 'i');
23+
const args = lines[1].split(/\s+/).map(Number);
24+
return {
25+
rx,
26+
max: args[0],
27+
delay: args[1],
28+
exp: args[2]
29+
};
30+
}
31+
32+
export async function retry(fn, key) {
33+
const c = rules.find(r => r.rx.test(key));
34+
if (c) {
35+
let delay = c.delay;
36+
for (let i = 0; i < c.max; i++) {
37+
try {
38+
return await fn();
39+
} catch (err) {
40+
console.warn(err);
41+
console.warn(`try again after ${delay}s`);
42+
}
43+
await timeout(delay * 1000);
44+
delay *= c.exp;
45+
}
46+
}
47+
return await fn();
48+
}

‎src/lib/timeout.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function timeout(t) {
2+
return new Promise(resolve => setTimeout(resolve, t));
3+
}

‎src/lib/url-map.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {pref} from "./pref.js";
2+
import {parseText} from "./readline.js";
23

34
let transforms = [];
45

@@ -12,13 +13,8 @@ pref.ready().then(() => {
1213
});
1314

1415
function update() {
15-
const lines = pref.get("urlMap").split(/\r?\n/g).filter(line =>
16-
line && /\S/.test(line) && !line.startsWith("#"));
17-
const newTransforms = [];
18-
for (let i = 0; i < lines.length; i += 2) {
19-
newTransforms.push(createTransform(lines[i], lines[i + 1]));
20-
}
21-
transforms = newTransforms;
16+
transforms = [...parseText(pref.get("urlMap"), 2)]
17+
.map(lines => createTransform(...lines));
2218
}
2319

2420
function createTransform(search, repl) {

‎src/options.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,14 @@ const root = createUI({
313313
label: _("optionUrlMapLabel"),
314314
learnMore: "https://github.com/eight04/image-picka#transform-url-with-regexp",
315315
className: "form-monospace"
316-
},
316+
},
317+
{
318+
type: "textarea",
319+
key: "retryOnFailure",
320+
label: _("optionRetryOnFailureLabel"),
321+
learnMore: "https://github.com/eight04/image-picka#retry-on-failure",
322+
className: "form-monospace"
323+
},
317324
{
318325
type: "textarea",
319326
key: "blacklist",

‎src/static/_locales/en/messages.json

+4
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@
291291
}
292292
}
293293
},
294+
"optionRetryOnFailureLabel": {
295+
"message": "Retry on failure",
296+
"description": "Label of retryOnFailure option"
297+
},
294298
"optionUrlMapLabel": {
295299
"message": "Transform URL with regexp",
296300
"description": "Label of urlMap option"

‎test/server.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,28 @@ const fs = require("fs");
66
const {parse: urlParse} = require("url");
77
const {Throttle} = require("stream-throttle");
88
const mime = require("mime");
9+
let i = 1;
910
const HANDLES = [
1011
{
1112
test: /^throttle\.png/,
1213
handle(req, res) {
1314
const image = fs.createReadStream(__dirname + "/test.png");
1415
image.pipe(new Throttle({rate: 100})).pipe(res);
1516
}
16-
}
17+
},
18+
{
19+
test: /503\.png/,
20+
handle(req, res) {
21+
res.setHeader("Cache-Control", "no-cache");
22+
if (i++ % 10) {
23+
res.writeHead(503);
24+
res.end('503');
25+
return;
26+
}
27+
const image = fs.createReadStream(__dirname + "/test.png");
28+
image.pipe(res);
29+
}
30+
}
1731
];
1832
const server = http.createServer((req, res) => {
1933
console.log(`${new Date} ${req.method} ${req.url}`);

0 commit comments

Comments
 (0)
Please sign in to comment.