lib/request: use async/await, simplify error handling

This commit is contained in:
Jannis R 2022-05-03 17:51:52 +02:00
parent b030eec1f5
commit 7765f9d7a1
No known key found for this signature in database
GPG key ID: 0FE83946296A88A5
3 changed files with 59 additions and 66 deletions

View file

@ -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,
}

View file

@ -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

View file

@ -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",