From 7765f9d7a1894ff089a9b4a4dfe27e7e3f4cfbaf Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 3 May 2022 17:51:52 +0200 Subject: [PATCH] lib/request: use async/await, simplify error handling --- lib/errors.js | 13 ------ lib/request.js | 111 ++++++++++++++++++++++++++----------------------- package.json | 1 - 3 files changed, 59 insertions(+), 66 deletions(-) diff --git a/lib/errors.js b/lib/errors.js index 59490ea1..ca7fc907 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -207,23 +207,10 @@ const byErrorCode = Object.assign(Object.create(null), { } }) -const addErrorInfo = (err, errorCode, errorText, responseId) => { - if (byErrorCode[errorCode]) { - Object.assign(err, byErrorCode[errorCode]) - if (errorCode) err.hafasErrorCode = errorCode - if (errorText) err.hafasErrorMessage = errorText - } else { - err.code = errorCode || null - err.message = errorText || errorCode || null - err.responseId = responseId || null - } -} - module.exports = { ACCESS_DENIED, INVALID_REQUEST, NOT_FOUND, SERVER_ERROR, byErrorCode, - addErrorInfo, } diff --git a/lib/request.js b/lib/request.js index d155ebbe..9b66186f 100644 --- a/lib/request.js +++ b/lib/request.js @@ -1,7 +1,5 @@ 'use strict' -const DEV = process.env.NODE_ENV === 'dev' - const ProxyAgent = require('https-proxy-agent') const {isIP} = require('net') const {Agent: HttpsAgent} = require('https') @@ -9,12 +7,11 @@ const roundRobin = require('@derhuerst/round-robin-scheduler') const {randomBytes} = require('crypto') const createHash = require('create-hash') const pick = require('lodash/pick') -const captureStackTrace = DEV ? require('capture-stack-trace') : () => {} const {stringify} = require('qs') const Promise = require('pinkie-promise') const {Request, fetch} = require('fetch-ponyfill')({Promise}) const {parse: parseContentType} = require('content-type') -const {addErrorInfo} = require('./errors') +const {byErrorCode} = require('./errors') const proxyAddress = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || null const localAddresses = process.env.LOCAL_ADDRESS || null @@ -66,16 +63,16 @@ const randomizeUserAgent = (userAgent) => { const md5 = input => createHash('md5').update(input).digest() -const request = (ctx, userAgent, reqData) => { +const request = async (ctx, userAgent, reqData) => { const {profile, opt} = ctx - const body = profile.transformReqBody(ctx, { + const rawReqBody = profile.transformReqBody(ctx, { // todo: is it `eng` actually? // RSAG has `deu` instead of `de` lang: opt.language || profile.defaultLanguage || 'en', svcReqL: [reqData] }) - Object.assign(body, pick(profile, [ + Object.assign(rawReqBody, pick(profile, [ 'client', // client identification 'ext', // ? 'ver', // HAFAS protocol version @@ -86,7 +83,7 @@ const request = (ctx, userAgent, reqData) => { agent: getAgent(), method: 'post', // todo: CORS? referrer policy? - body: JSON.stringify(body), + body: JSON.stringify(rawReqBody), headers: { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, br, deflate', @@ -129,56 +126,66 @@ const request = (ctx, userAgent, reqData) => { const fetchReq = new Request(url, req) profile.logRequest(ctx, fetchReq, reqId) - // Async stack traces are not supported everywhere yet, so we create our own. - const err = new Error() - err.isHafasError = true // todo: rename to `isHafasClientError` - err.request = req.body // todo: commit as bugfix - err.url = url - captureStackTrace(err) + const res = await fetch(url, req) - return fetch(fetchReq) - .then((res) => { - err.statusCode = res.status - if (!res.ok) { - err.message = res.statusText + // Async stack traces are not supported everywhere yet, so we create our own. + const errProps = { + isHafasError: true, // todo: rename to `isHafasClientError` + statusCode: res.status, + request: req.body, // todo [breaking]: change to fetchReq + url: url, + } + + if (!res.ok) { + const err = new Error(res.statusText) + Object.assign(err, errProps) + throw err + } + + let cType = res.headers.get('content-type') + if (cType) { + const {type} = parseContentType(cType) + if (type !== 'application/json') { + const err = new Error('invalid response content-type: ' + cType) + err.response = res throw err } + } - let cType = res.headers.get('content-type') - if (cType) { - const {type} = parseContentType(cType) - if (type !== 'application/json') { - const err = new Error('invalid response content-type: ' + cType) - err.response = res - throw err - } + const body = await res.text() + profile.logResponse(ctx, res, body, reqId) + + const b = JSON.parse(body) + if (b.err) errProps.hafasErrorCode = b.err + if (b.errTxt) errProps.hafasErrorMessage = b.errTxt + if (b.id) errProps.responseId = b.id + if (b.err && b.err !== 'OK') { + const err = new Error(b.errTxt || b.err) + Object.assign(err, errProps) + if (b.err in byErrorCode) { + Object.assign(err, byErrorCode[b.err]) } + throw err + } + if (!b.svcResL || !b.svcResL[0]) { + const err = new Error('invalid/unsupported response structure') + Object.assign(err, errProps) + throw err + } + if (b.svcResL[0].err !== 'OK') { + const err = new Error(b.svcResL[0].errTxt || b.svcResL[0].err) + Object.assign(err, errProps) + if (b.svcResL[0].err in byErrorCode) { + Object.assign(err, byErrorCode[b.svcResL[0].err]) + } + throw err + } - return res.text() - .then((body) => { - profile.logResponse(ctx, res, body, reqId) - const b = JSON.parse(body) - - if (b.err && b.err !== 'OK') { - addErrorInfo(err, b.err, b.errTxt, b.id) - throw err - } - if (!b.svcResL || !b.svcResL[0]) { - err.message = 'invalid response' - throw err - } - if (b.svcResL[0].err !== 'OK') { - addErrorInfo(err, b.svcResL[0].err, b.svcResL[0].errTxt, b.id) - throw err - } - - const svcRes = b.svcResL[0].res - return { - res: svcRes, - common: profile.parseCommon({...ctx, res: svcRes}), - } - }) - }) + const svcRes = b.svcResL[0].res + return { + res: svcRes, + common: profile.parseCommon({...ctx, res: svcRes}), + } } module.exports = request diff --git a/package.json b/package.json index 2e55cf82..8f03119c 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "dependencies": { "@derhuerst/br2nl": "^1.0.0", "@derhuerst/round-robin-scheduler": "^1.0.4", - "capture-stack-trace": "^1.0.0", "content-type": "^1.0.4", "create-hash": "^1.2.0", "fetch-ponyfill": "^7.0.0",