-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdialers.ts
115 lines (104 loc) · 3.44 KB
/
dialers.ts
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
/**
* API to help satisfy HTTP requests by providing network connections.
*/
export interface Dialer {
dial(target: URL): Promise<Deno.Conn>;
}
/**
* Satisfies HTTP requests by creating a basic plaintext TCP connection.
*/
export class TcpDialer implements Dialer {
async dial(target: URL): Promise<Deno.Conn> {
return await Deno.connect({
...resolveHostPort(target, "80"),
});
}
}
/**
* Satisfies HTTPS requests by creating a TLS-encrypted TCP connection.
* CA and servername options can be set on the constructor.
*
* Deno's TLS API became stabilized in Deno v1.16.
*/
export class TlsDialer implements Dialer {
constructor(
public readonly opts?: Deno.StartTlsOptions | Deno.ConnectTlsOptions,
) {}
async dial(target: URL): Promise<Deno.Conn> {
// If the options include a separete SNI host, we need to use startTls.
if (this.opts?.hostname) {
const conn = await Deno.connect({
...resolveHostPort(target, "443"),
});
const tlsConn = await Deno.startTls(conn, {
hostname: target.hostname,
...this.opts,
});
return tlsConn;
// Else, we can use connectTls and get mTLS support.
} else {
return await Deno.connectTls({
...resolveHostPort(target, "443"),
...this.opts,
});
}
}
}
/**
* Satisfies HTTP/HTTPS requests traditionally using the appropriate dialer.
* If you need to configure a specific dialer, use it directly instead.
*
* To access UNIX servers,
* URL-encode the socket path into the host part of an `http+unix:` URL.
* For example:
* http+unix://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json
* Note that this URL parsing may break in future versions of Deno.
*/
export class AutoDialer implements Dialer {
private readonly tlsDialer = new TlsDialer();
private readonly tcpDialer = new TcpDialer();
dial(target: URL): Promise<Deno.Conn> {
switch (target.protocol) {
case 'http:': return this.tcpDialer.dial(target);
case 'https:': return this.tlsDialer.dial(target);
case 'http+unix:': {
// Approximation of https://github.com/whatwg/url/issues/577#issuecomment-968496984 et al
// Browsers handle these URLs differently, but this works as of Deno v1.16
if (target.port) throw new Error(`UNIX Domain Socket URLs cannot have a port`);
const sockPath = decodeURIComponent(target.hostname);
return new UnixDialer(sockPath).dial();
};
default: throw new Error(`Protocol not implemented: ${JSON.stringify(target.protocol)}`);
}
}
}
/**
* Satisfies HTTP requests by creating a plaintext UNIX Stream socket.
*
* A socket location MUST be specified, and will be used for all connections.
* Note that the URL given for the request will still be used for the Host header.
*/
export class UnixDialer implements Dialer {
constructor(
public readonly socketPath: string,
) {
if (!socketPath) throw new Error(`No UNIX socket path given to UnixDialer`);
}
async dial(): Promise<Deno.Conn> {
return await Deno.connect({
transport: "unix",
path: this.socketPath,
});
}
}
function resolveHostPort(target: URL, defaultPort: string) {
const givenPort = target.port || defaultPort;
const parsedPort = parseInt(givenPort);
if (givenPort != parsedPort.toFixed(0)) {
throw new Error(`Failed to parse an integer out of port ${JSON.stringify(givenPort)}`);
}
return {
hostname: target.hostname,
port: parsedPort,
};
}