@qaick/http-executor 0.1.0-alpha.3 → 0.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -13,6 +13,8 @@ export interface HttpTransport {
|
|
|
13
13
|
timeoutMs?: number;
|
|
14
14
|
}): Promise<HttpResponse>;
|
|
15
15
|
}
|
|
16
|
+
/** Host-owned cookies that already exist before a QAick execution starts. */
|
|
17
|
+
export type HttpAmbientCookieProvider = (url: string) => Promise<string | undefined>;
|
|
16
18
|
/** Internal Runtime-to-HTTP execution key. It is not part of persisted Action data. */
|
|
17
19
|
export declare const HTTP_EXECUTION_ID_KEY = "__qaick_execution_id";
|
|
18
20
|
export interface HttpCookie {
|
|
@@ -34,7 +36,8 @@ export declare class HttpCookieJar {
|
|
|
34
36
|
export declare class HttpExecutionSession implements HttpTransport {
|
|
35
37
|
private readonly transport;
|
|
36
38
|
readonly cookies: HttpCookieJar;
|
|
37
|
-
|
|
39
|
+
private readonly ambientCookies?;
|
|
40
|
+
constructor(transport: HttpTransport, cookies?: HttpCookieJar, ambientCookies?: HttpAmbientCookieProvider | undefined);
|
|
38
41
|
request(url: string, init: {
|
|
39
42
|
method: string;
|
|
40
43
|
body?: unknown;
|
|
@@ -39,21 +39,39 @@ export class HttpCookieJar {
|
|
|
39
39
|
export class HttpExecutionSession {
|
|
40
40
|
transport;
|
|
41
41
|
cookies;
|
|
42
|
-
|
|
42
|
+
ambientCookies;
|
|
43
|
+
constructor(transport, cookies = new HttpCookieJar(), ambientCookies) {
|
|
43
44
|
this.transport = transport;
|
|
44
45
|
this.cookies = cookies;
|
|
46
|
+
this.ambientCookies = ambientCookies;
|
|
45
47
|
}
|
|
46
48
|
async request(url, init) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
let currentUrl = url;
|
|
50
|
+
let currentInit = { ...init };
|
|
51
|
+
for (let redirect = 0; redirect <= 10; redirect += 1) {
|
|
52
|
+
const headers = { ...(currentInit.headers ?? {}) };
|
|
53
|
+
const cookieHeader = headerName(headers, 'cookie');
|
|
54
|
+
const managed = this.cookies.header(currentUrl);
|
|
55
|
+
const ambient = this.ambientCookies ? await this.ambientCookies(currentUrl).catch(() => undefined) : undefined;
|
|
56
|
+
const merged = mergeCookieHeaders(cookieHeader ? headers[cookieHeader] : undefined, managed, ambient);
|
|
57
|
+
if (merged)
|
|
58
|
+
headers[cookieHeader ?? 'cookie'] = merged;
|
|
59
|
+
const response = await this.transport.request(currentUrl, { ...currentInit, headers });
|
|
60
|
+
const setCookie = response.headers && headerValue(response.headers, 'set-cookie');
|
|
61
|
+
this.cookies.store(currentUrl, setCookie);
|
|
62
|
+
if (!isRedirect(response.status) || !response.headers)
|
|
63
|
+
return response;
|
|
64
|
+
const location = headerValue(response.headers, 'location');
|
|
65
|
+
if (!location)
|
|
66
|
+
return response;
|
|
67
|
+
if (redirect === 10)
|
|
68
|
+
return response;
|
|
69
|
+
const nextUrl = new URL(location, currentUrl).toString();
|
|
70
|
+
const nextMethod = response.status === 303 || ((response.status === 301 || response.status === 302) && currentInit.method.toUpperCase() === 'POST') ? 'GET' : currentInit.method;
|
|
71
|
+
currentUrl = nextUrl;
|
|
72
|
+
currentInit = nextMethod === 'GET' ? { ...currentInit, method: nextMethod, body: undefined } : { ...currentInit, method: nextMethod };
|
|
73
|
+
}
|
|
74
|
+
throw new Error('HTTP redirect limit exceeded.');
|
|
57
75
|
}
|
|
58
76
|
}
|
|
59
77
|
export class HttpActionError extends Error {
|
|
@@ -73,9 +91,10 @@ function headerValue(headers, expected) {
|
|
|
73
91
|
const name = Object.keys(headers).find(key => key.toLowerCase() === expected);
|
|
74
92
|
return name ? headers[name] : undefined;
|
|
75
93
|
}
|
|
76
|
-
function
|
|
94
|
+
function isRedirect(status) { return status === 301 || status === 302 || status === 303 || status === 307 || status === 308; }
|
|
95
|
+
function mergeCookieHeaders(explicit, managed, ambient) {
|
|
77
96
|
const cookies = new Map();
|
|
78
|
-
for (const value of [managed, explicit]) {
|
|
97
|
+
for (const value of [ambient, managed, explicit]) {
|
|
79
98
|
for (const item of value?.split(';') ?? []) {
|
|
80
99
|
const separator = item.indexOf('=');
|
|
81
100
|
if (separator <= 0)
|