1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { HafasError, byErrorCode } from 'hafas-client/lib/errors.js';
const
randomBytesHexString = length => [...Array(length)].map(
() => Math.floor(Math.random() * 16).toString(16)
).join('')
export const
checkIfHafasResponseIsOk = (_) => {
const {
body,
errProps: baseErrProps,
} = _;
const errProps = { ...baseErrProps };
if (body.id) errProps.hafasResponseId = body.id;
// Because we want more accurate stack traces, we don't construct the error here,
// but only return the constructor & error message.
const getError = (_) => {
// mutating here is ugly but pragmatic
if (_.errTxt) errProps.hafasMessage = _.errTxt;
if (_.errTxtOut) errProps.hafasDescription = _.errTxtOut;
if (_.err in byErrorCode) return byErrorCode[_.err];
return {
Error: HafasError,
message: body.errTxt || 'unknown error',
props: {},
};
};
if (body.err && body.err !== 'OK') {
const { Error: HafasError, message, props } = getError(body);
throw new HafasError(message, body.err, { ...errProps, ...props });
}
if (!body.svcResL || !body.svcResL[0])
throw new HafasError('invalid/unsupported response structure', null, errProps);
if (body.svcResL[0].err !== 'OK') {
const {Error: HafasError, message, props} = getError(body.svcResL[0]);
throw new HafasError(message, body.svcResL[0].err, { ...errProps, ...props });
}
},
hafasRequest = async (ctx, userAgent, reqData) => {
const {profile, opt} = ctx;
if (profile.addChecksum)
throw new Error('profile.addChecksum set but not implemented!');
if (profile.addMicMac)
throw new Error('profile.addMicMac set but not implemented!');
const
rawReqBody = profile.transformReqBody(ctx, {
// todo: is it `eng` actually?
// RSAG has `deu` instead of `de`
lang: opt.language || profile.defaultLanguage || 'en',
svcReqL: [reqData],
client: profile.client, // client identification
ext: profile.ext, // ?
ver: profile.ver, // HAFAS protocol version
auth: profile.auth, // static authentication
}),
reqId = randomBytesHexString(3).toString('hex'),
req = profile.transformReq(ctx, {
agent: null,
method: 'post',
// todo: CORS? referrer policy?
body: JSON.stringify(rawReqBody),
headers: {
'Content-Type': 'application/json',
'Accept-Encoding': 'gzip, br, deflate',
'Accept': 'application/json',
'user-agent': userAgent,
'connection': 'keep-alive', // prevent excessive re-connecting
},
redirect: 'follow',
query: {},
}),
url = `/hafas/${profile.client.id}?${(new URLSearchParams(req.query)).toString()}`,
fetchReq = new Request(url, req),
res = await fetch(url, req),
errProps = {
// todo [breaking]: assign as non-enumerable property
request: fetchReq,
// todo [breaking]: assign as non-enumerable property
response: res,
url,
};
if (!res.ok) {
// todo [breaking]: make this a FetchError or a HafasClientError?
const err = new Error(res.statusText);
Object.assign(err, errProps);
throw err;
}
let cType = res.headers.get('content-type');
if (!cType.startsWith('application/json'))
throw new HafasError('invalid/unsupported response content-type: ' + cType, null, errProps);
const body = JSON.parse(await res.text())
checkIfHafasResponseIsOk({ body, errProps });
const svcRes = body.svcResL[0].res;
return {
res: svcRes,
common: profile.parseCommon({...ctx, res: svcRes}),
};
};