Skip to content

Commit 3e1e798

Browse files
authored
handle server timeouts (#52)
Some sites will timeout and extract-css then fails but keeps the browser running in the background. This commit fixes that.
1 parent 667f444 commit 3e1e798

File tree

2 files changed

+48
-19
lines changed

2 files changed

+48
-19
lines changed

src/index.js

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,17 @@ InvalidUrlError.prototype = Error.prototype
1212
/**
1313
* @param {string} url URL to get CSS from
1414
* @param {string} waitUntil https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pagegotourl-options
15+
* @param {string} timeout https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pagegotourl-options
16+
* @param {string} origins Can either be 'include' or 'exlude'
17+
* @param {string} inlineStyles Can either be 'include' or 'exlude'
1518
* @returns {string} All CSS that was found
1619
*/
17-
module.exports = async (url, {waitUntil = 'networkidle0', origins = 'exclude', inlineStyles = 'include'} = {}) => {
20+
module.exports = async (url, {
21+
waitUntil = 'networkidle0',
22+
timeout = 10000,
23+
origins = 'exclude',
24+
inlineStyles = 'include'
25+
} = {}) => {
1826
// Setup a browser instance
1927
const browser = await puppeteer.launch()
2028

@@ -26,29 +34,40 @@ module.exports = async (url, {waitUntil = 'networkidle0', origins = 'exclude', i
2634
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0')
2735
await page.coverage.startCSSCoverage()
2836
url = normalizeUrl(url, {stripWWW: false})
29-
const response = await page.goto(url, {waitUntil})
37+
38+
let response
39+
40+
// Explicit try-catch for when pages timeout
41+
try {
42+
response = await page.goto(url, {
43+
waitUntil,
44+
timeout
45+
})
46+
} catch (error) {
47+
// In case of timeouts
48+
await browser.close()
49+
50+
throw error
51+
}
3052

3153
// Make sure that we only try to extract CSS from valid pages.
3254
// Bail out if the response is an invalid request (400, 500)
3355
if (response.status() >= 400) {
3456
await browser.close() // Don't leave any resources behind
3557

36-
return Promise.reject(
37-
new InvalidUrlError({
38-
url,
39-
statusCode: response.status(),
40-
statusText: response.statusText()
41-
})
42-
)
58+
throw new InvalidUrlError({
59+
url,
60+
statusCode: response.status(),
61+
statusText: response.statusText()
62+
})
4363
}
4464

4565
// If the response is a CSS file, return that file
4666
// instead of running our complicated setup
4767
const headers = response.headers()
4868

4969
if (headers['content-type'].includes('text/css')) {
50-
const css = await response.text()
51-
return Promise.resolve(css)
70+
return response.text()
5271
}
5372

5473
const coverage = await page.coverage.stopCSSCoverage()
@@ -95,6 +114,8 @@ module.exports = async (url, {waitUntil = 'networkidle0', origins = 'exclude', i
95114
.map(css => ({type: 'inline', href: url, css}))
96115
}
97116

117+
await browser.close()
118+
98119
const links = coverage
99120
// Filter out the <style> tags that were found in the coverage
100121
// report since we've conducted our own search for them.
@@ -107,21 +128,17 @@ module.exports = async (url, {waitUntil = 'networkidle0', origins = 'exclude', i
107128
type: 'link-or-import'
108129
}))
109130

110-
await browser.close()
111-
112131
const css = links
113132
.concat(styleSheetsApiCss)
114133
.concat(inlineStyles === 'exclude' ? [] : inlineCss)
115134

116135
// Return the complete structure ...
117136
if (origins === 'include') {
118-
return Promise.resolve(css)
137+
return css
119138
}
120139

121140
// ... or return all CSS as a single String
122-
return Promise.resolve(
123-
css
124-
.map(({css}) => css)
125-
.join('\n')
126-
)
141+
return css
142+
.map(({css}) => css)
143+
.join('\n')
127144
}

test/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,15 @@ test('it rejects if the url has an HTTP error status', async t => {
145145
test('it rejects on an invalid url', async t => {
146146
await t.throwsAsync(extractCss('site.example'))
147147
})
148+
149+
test('it rejects on server timing out', async t => {
150+
server.get('/timeout-page', (req, res) => {
151+
setTimeout(function () {
152+
res.status(500).send()
153+
}, 5000)
154+
})
155+
const timoutUrl = server.url + '/timeout-page'
156+
await t.throwsAsync(extractCss(timoutUrl, {timeout: 4500}), {
157+
message: 'Navigation timeout of 4500 ms exceeded'
158+
})
159+
})

0 commit comments

Comments
 (0)