Skip to content

Commit

Permalink
Implement ALPN support for the Deno adapter. (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
1st1 authored Jan 20, 2022
1 parent 11c70df commit 4031cb1
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deno-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:

- uses: denoland/setup-deno@v1
with:
deno-version: v1.14.x
deno-version: v1.17.x

- name: Install dev deps
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
uses: denoland/setup-deno@v1
if: steps.release.outputs.version == 0
with:
deno-version: v1.14.x
deno-version: v1.17.x

- name: Install dev deps
if: steps.release.outputs.version == 0
Expand Down
87 changes: 58 additions & 29 deletions src/adapter.deno.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {process} from "https://deno.land/std@0.108.0/node/process.ts";
import {Buffer} from "https://deno.land/std@0.108.0/node/buffer.ts";
import * as crypto from "https://deno.land/std@0.108.0/node/crypto.ts";
import {process} from "https://deno.land/std@0.114.0/node/process.ts";
import {Buffer} from "https://deno.land/std@0.114.0/node/buffer.ts";
import * as crypto from "https://deno.land/std@0.114.0/node/crypto.ts";
import {
Sha256,
HmacSha256,
} from "https://deno.land/std@0.108.0/hash/sha256.ts";
import path from "https://deno.land/std@0.108.0/node/path.ts";
} from "https://deno.land/std@0.114.0/hash/sha256.ts";
import path from "https://deno.land/std@0.114.0/node/path.ts";
import * as _fs from "https://deno.land/[email protected]/fs/mod.ts";
import EventEmitter from "https://deno.land/[email protected]/node/events.ts";
import util from "https://deno.land/[email protected]/node/util.ts";
import EventEmitter from "https://deno.land/[email protected]/node/events.ts";
import util from "https://deno.land/[email protected]/node/util.ts";
import {iterateReader} from "https://deno.land/[email protected]/streams/conversion.ts";

export {Buffer, path, process, util, crypto};

Expand Down Expand Up @@ -138,22 +139,10 @@ export namespace net {
on(eventName: "close", listener: () => void): this;
}

export class Socket extends EventEmitter {
private _conn: Deno.Conn | null = null;
private _readIter: AsyncIterableIterator<Uint8Array> | null = null;
private _paused = true;

constructor(pconn: Promise<Deno.Conn>) {
super();
pconn
.then(conn => {
this._conn = conn;
this._readIter = Deno.iter(conn);
this.emit("connect");
this.resume();
})
.catch(e => this.emit("error", e));
}
export class BaseSocket<T extends Deno.Conn> extends EventEmitter {
protected _conn: T | null = null;
protected _readIter: AsyncIterableIterator<Uint8Array> | null = null;
protected _paused = true;

setNoDelay() {
// No deno api for this
Expand Down Expand Up @@ -212,11 +201,26 @@ export namespace net {
}
}
}

export class Socket extends BaseSocket<Deno.Conn> {
constructor(pconn: Promise<Deno.Conn>) {
super();
pconn
.then(conn => {
this._conn = conn;
this._readIter = iterateReader(conn);
this.emit("connect");
this.resume();
})
.catch(e => {
this.emit("error", e);
});
}
}
}

// TODO: deno's TLS implementation doesn't currently support ALPN.
export namespace tls {
export function connect(options: tls.ConnectionOptions): net.Socket {
export function connect(options: tls.ConnectionOptions): tls.TLSSocket {
if (options.host == null) {
throw new Error("host option must be set");
}
Expand All @@ -225,7 +229,14 @@ export namespace tls {
throw new Error("port option must be set");
}

return net.createConnection(options.port, options.host);
const conn = Deno.connectTls({
port: options.port,
hostname: options.host,
alpnProtocols: options.ALPNProtocols,
caCerts: typeof options.ca === "string" ? [options.ca] : options.ca,
});

return new TLSSocket(conn);
}

export function checkServerIdentity(
Expand All @@ -244,9 +255,27 @@ export namespace tls {
rejectUnauthorized?: boolean;
}

export class TLSSocket extends net.Socket {
get alpnProtocol(): string {
throw new Error("deno does not support ALPN");
export class TLSSocket extends net.BaseSocket<Deno.TlsConn> {
private _alpnProtocol: string | null = null;

constructor(pconn: Promise<Deno.TlsConn>) {
super();
pconn
.then(async conn => {
const handshake = await conn.handshake();
this._alpnProtocol = handshake.alpnProtocol;
this._conn = conn;
this._readIter = iterateReader(conn);
this.emit("secureConnect");
this.resume();
})
.catch(e => {
this.emit("error", e);
});
}

get alpnProtocol(): string | false {
return this._alpnProtocol ?? false;
}
}
}
4 changes: 2 additions & 2 deletions src/globals.deno.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {Buffer} from "https://deno.land/std@0.108.0/node/buffer.ts";
export {process} from "https://deno.land/std@0.108.0/node/process.ts";
export {Buffer} from "https://deno.land/std@0.114.0/node/buffer.ts";
export {process} from "https://deno.land/std@0.114.0/node/process.ts";
56 changes: 36 additions & 20 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ function setStringCodecs(codecs: string[], client: Client) {
);
}

class CancelTransaction extends Error {}

test("query: basic scalars", async () => {
const con = getClient();
let res: any;
Expand Down Expand Up @@ -1219,30 +1221,38 @@ test("fetch: uuid", async () => {
test("fetch: enum", async () => {
const client = getClient();

await client.withRetryOptions({attempts: 1}).transaction(async tx => {
await tx.execute(`
try {
await client.withRetryOptions({attempts: 1}).transaction(async tx => {
await tx.execute(`
CREATE SCALAR TYPE MyEnum EXTENDING enum<"A", "B">;
`);

// await tx.query("declare savepoint s1");
// await tx
// .querySingle("SELECT <MyEnum><str>$0", ["Z"])
// .then(() => {
// throw new Error("an exception was expected");
// })
// .catch((e) => {
// expect(e.toString()).toMatch(/invalid input value for enum/);
// });
// await tx.query("rollback to savepoint s1");

let ret = await tx.querySingle("SELECT <MyEnum><str>$0", ["A"]);
expect(ret).toBe("A");

ret = await tx.querySingle("SELECT <MyEnum>$0", ["A"]);
expect(ret).toBe("A");
});
// await tx.query("declare savepoint s1");
// await tx
// .querySingle("SELECT <MyEnum><str>$0", ["Z"])
// .then(() => {
// throw new Error("an exception was expected");
// })
// .catch((e) => {
// expect(e.toString()).toMatch(/invalid input value for enum/);
// });
// await tx.query("rollback to savepoint s1");

await client.close();
let ret = await tx.querySingle("SELECT <MyEnum><str>$0", ["A"]);
expect(ret).toBe("A");

ret = await tx.querySingle("SELECT <MyEnum>$0", ["A"]);
expect(ret).toBe("A");

throw new CancelTransaction();
});
} catch (e) {
if (!(e instanceof CancelTransaction)) {
throw e;
}
} finally {
await client.close();
}
});

test("fetch: namedtuple", async () => {
Expand Down Expand Up @@ -1569,7 +1579,13 @@ test("fetch/optimistic cache invalidation", async () => {
const res = await tx.querySingle(query);
expect(res).toBe(123);
}

throw new CancelTransaction();
});
} catch (e) {
if (!(e instanceof CancelTransaction)) {
throw e;
}
} finally {
await client.close();
}
Expand Down
3 changes: 3 additions & 0 deletions test/deno.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ test("run deno test", async () => {
"--allow-env",
"--allow-read",
"--allow-write",
"--unsafely-ignore-certificate-errors",
"test/deno",
],
{
env: process.env,
timeout: 120_000,
},
(error, stdout, stderr) => {
if (error) {
Expand Down Expand Up @@ -78,6 +80,7 @@ test("deno check", async () => {
["eval", "--unstable", 'import * as edgedb from "./edgedb-deno/mod.ts"'],
{
env: process.env,
timeout: 60_000,
},
(error, stdout, stderr) => {
if (error) {
Expand Down
23 changes: 6 additions & 17 deletions test/globalSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ export const getServerCommand = (statusFile: string): string[] => {
const helpCmd = [...args, "--help"];
const help = child_process.execSync(helpCmd.join(" "));

if (help.includes("--generate-self-signed-cert")) {
if (help.includes("--tls-cert-mode")) {
args.push("--tls-cert-mode=generate_self_signed");
} else if (help.includes("--generate-self-signed-cert")) {
args.push("--generate-self-signed-cert");
}

Expand All @@ -81,6 +83,8 @@ export const getServerCommand = (statusFile: string): string[] => {

args = [
...args,
"--bind-address=127.0.0.1",
"--bind-address=::1", // deno on some platforms resolves localhost to ::1
"--temp-dir",
"--testmode",
"--port=auto",
Expand Down Expand Up @@ -163,7 +167,7 @@ export const startServer = async (
}

const config: ConnectConfig = {
host: "127.0.0.1",
host: "localhost",
port: runtimeData.port,
user: "edgedb",
password: "edgedbtest",
Expand Down Expand Up @@ -211,14 +215,6 @@ export default async () => {
// tslint:disable-next-line
console.log("\nStarting EdgeDB test cluster...");

const denoStatusFile = generateStatusFileName("deno");
console.log("Deno status file:", denoStatusFile);
const denoArgs = getServerCommand(getWSLPath(denoStatusFile));
if (denoArgs.includes("--generate-self-signed-cert")) {
denoArgs.push("--binary-endpoint-security=optional");
}
const denoPromise = startServer(denoArgs, denoStatusFile);

const statusFile = generateStatusFileName("node");
console.log("Node status file:", statusFile);

Expand All @@ -228,17 +224,10 @@ export default async () => {
// @ts-ignore
global.edgedbProc = proc;

const {proc: denoProc, config: denoConfig} = await denoPromise;
// @ts-ignore
global.edgedbDenoProc = denoProc;

process.env._JEST_EDGEDB_CONNECT_CONFIG = JSON.stringify(config);
process.env._JEST_EDGEDB_DENO_CONNECT_CONFIG = JSON.stringify(denoConfig);

// @ts-ignore
global.edgedbConn = await connectToServer(config);
// @ts-ignore
global.edgedbDenoConn = await connectToServer(denoConfig);

// tslint:disable-next-line
console.log(`EdgeDB test cluster is up [port: ${config.port}]...`);
Expand Down
8 changes: 2 additions & 6 deletions test/globalTeardown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,8 @@ export default async () => {
console.log("Shutting down EdgeDB test cluster...");

try {
await Promise.all([
// @ts-ignore
shutdown(global.edgedbProc, global.edgedbConn),
// @ts-ignore
shutdown(global.edgedbDenoProc, global.edgedbDenoConn),
]);
// @ts-ignore
await shutdown(global.edgedbProc, global.edgedbConn);
} finally {
// tslint:disable-next-line
console.log("EdgeDB test cluster is down...");
Expand Down
6 changes: 1 addition & 5 deletions test/testbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ export const isDeno =
function _getOpts(opts: ConnectOptions): ConnectOptions {
let config;
try {
if (isDeno) {
config = JSON.parse(process.env._JEST_EDGEDB_DENO_CONNECT_CONFIG || "");
} else {
config = JSON.parse(process.env._JEST_EDGEDB_CONNECT_CONFIG || "");
}
config = JSON.parse(process.env._JEST_EDGEDB_CONNECT_CONFIG || "");
} catch {
throw new Error("EdgeDB Jest test environment is not initialized");
}
Expand Down
6 changes: 3 additions & 3 deletions tools/compileForDeno.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {walk, ensureDir} from "https://deno.land/std@0.108.0/fs/mod.ts";
import {walk, ensureDir} from "https://deno.land/std@0.114.0/fs/mod.ts";
import {
join,
relative,
dirname,
basename,
} from "https://deno.land/std@0.108.0/path/posix.ts";
import {createRequire} from "https://deno.land/std@0.108.0/node/module.ts";
} from "https://deno.land/std@0.114.0/path/posix.ts";
import {createRequire} from "https://deno.land/std@0.114.0/node/module.ts";

const require = createRequire(import.meta.url);

Expand Down

0 comments on commit 4031cb1

Please sign in to comment.