limit the size of response we will accept (#2726)
limit the size of response we will accept
This commit is contained in:
@@ -10,7 +10,11 @@ const makeBadge = require('../gh-badges/lib/make-badge')
|
||||
const analytics = require('./analytics')
|
||||
const { makeSend } = require('./result-sender')
|
||||
const queryString = require('query-string')
|
||||
const { Inaccessible } = require('../services/errors')
|
||||
const {
|
||||
Inaccessible,
|
||||
InvalidResponse,
|
||||
ShieldsRuntimeError,
|
||||
} = require('../services/errors')
|
||||
const { setCacheHeaders } = require('../services/cache-headers')
|
||||
|
||||
// We avoid calling the vendor's server for computation of the information in a
|
||||
@@ -64,6 +68,7 @@ function flattenQueryParams(queryParams) {
|
||||
// the service uses
|
||||
// - cacheLength: An optional badge or category-specific cache length
|
||||
// (in number of seconds) to be used in preference to the default
|
||||
// - fetchLimitBytes: A limit on the response size we're willing to parse
|
||||
//
|
||||
// For safety, the service must declare the query parameters it wants to use.
|
||||
// Only the declared parameters (and the global parameters) are provided to
|
||||
@@ -83,7 +88,10 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
|
||||
}
|
||||
|
||||
const allowedKeys = flattenQueryParams(handlerOptions.queryParams)
|
||||
const { cacheLength: serviceCacheLengthSeconds } = handlerOptions
|
||||
const {
|
||||
cacheLength: serviceCacheLengthSeconds,
|
||||
fetchLimitBytes,
|
||||
} = handlerOptions
|
||||
|
||||
return (queryParams, match, end, ask) => {
|
||||
const reqTime = new Date()
|
||||
@@ -167,7 +175,8 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
|
||||
options.headers['User-Agent'] =
|
||||
options.headers['User-Agent'] || 'Shields.io'
|
||||
|
||||
request(options, (err, res, body) => {
|
||||
let bufferLength = 0
|
||||
const r = request(options, (err, res, body) => {
|
||||
if (res != null && res.headers != null) {
|
||||
const cacheControl = res.headers['cache-control']
|
||||
if (cacheControl != null) {
|
||||
@@ -181,6 +190,18 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
|
||||
}
|
||||
callback(err, res, body)
|
||||
})
|
||||
r.on('data', chunk => {
|
||||
bufferLength += chunk.length
|
||||
if (bufferLength > fetchLimitBytes) {
|
||||
r.abort()
|
||||
r.emit(
|
||||
'error',
|
||||
new InvalidResponse({
|
||||
prettyMessage: 'Maximum response size exceeded',
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Wrapper around `cachingRequest` that returns a promise rather than
|
||||
@@ -189,9 +210,13 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
|
||||
new Promise((resolve, reject) => {
|
||||
cachingRequest(uri, options, (err, res, buffer) => {
|
||||
if (err) {
|
||||
// Wrap the error in an Inaccessible so it can be identified
|
||||
// by the BaseService handler.
|
||||
reject(new Inaccessible({ underlyingError: err }))
|
||||
if (err instanceof ShieldsRuntimeError) {
|
||||
reject(err)
|
||||
} else {
|
||||
// Wrap the error in an Inaccessible so it can be identified
|
||||
// by the BaseService handler.
|
||||
reject(new Inaccessible({ underlyingError: err }))
|
||||
}
|
||||
} else {
|
||||
resolve({ res, buffer })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user