Skip to content
This repository was archived by the owner on Feb 6, 2024. It is now read-only.

Commit 743e547

Browse files
committed
wai-lambda: add main.js
1 parent 131d2cd commit 743e547

File tree

4 files changed

+124
-13
lines changed

4 files changed

+124
-13
lines changed

infra/default.nix

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,8 @@ with { pkgs = import ./nix {}; };
55
# -> https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html
66

77
rec
8-
{ function = pkgs.runCommand "build-function" {}
9-
''
10-
cp ${./main.js} main.js
11-
# Can't be called 'main' otherwise lambda tries to load it
12-
cp ${handler}/bin/deckdeckgo-handler main_hs
13-
mkdir $out
14-
${pkgs.zip}/bin/zip -r $out/function.zip main.js main_hs
15-
'';
8+
{ function =
9+
pkgs.wai-lambda.wai-lambda-js-build-lambda "${handler}/bin/deckdeckgo-handler";
1610

1711
handler = pkgs.haskellPackagesStatic.deckdeckgo-handler;
18-
wai-lambda = pkgs.haskellPackages.wai-lambda;
1912
}

infra/nix/default.nix

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
with rec
33
{ sources = import ./sources.nix;
44
pkgs = import sources.nixpkgs {};
5+
wai-lambda = pkgs.callPackage ../wai-lambda/nix/packages.nix {};
56

67
pkgsStatic =
78
(import "${sources.static-haskell-nix}/survey"
@@ -23,9 +24,7 @@ with rec
2324

2425
super //
2526
mkPackage "deckdeckgo-handler" ../handler //
26-
(
27-
with { wai-lambda = pkgs'.callPackage ../wai-lambda/nix/packages.nix {}; };
28-
mkPackage "wai-lambda" wai-lambda.wai-lambda-source
27+
( mkPackage "wai-lambda" wai-lambda.wai-lambda-source
2928
);
3029
};
3130
normalHaskellPackages = pkgsStatic.pkgsMusl.haskellPackages.override
@@ -41,6 +40,6 @@ with rec
4140
};
4241

4342
pkgs //
44-
{ inherit haskellPackagesStatic haskellPackages sources;
43+
{ inherit haskellPackagesStatic haskellPackages sources wai-lambda;
4544
inherit (import sources.niv {}) niv;
4645
}

infra/wai-lambda/main.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
'use strict'
2+
3+
const execFile = require('child_process').execFile;
4+
const crypto = require('crypto');
5+
const fs = require('fs');
6+
const path = require('path');
7+
const os = require('os');
8+
9+
let child = null;
10+
11+
// Retrieve the singleton Haskell process
12+
function getHaskellProcess() {
13+
14+
if (child == null) {
15+
16+
child = execFile(path.join(process.env['LAMBDA_TASK_ROOT'],'main_hs'));
17+
18+
console.log('Exec-ed child');
19+
20+
// Set proper encoding
21+
child.stdin.setEncoding('utf-8');
22+
child.stdout.setEncoding('utf-8');
23+
24+
// Forward Haskell logs to CloudWatch
25+
child.stdout.on('data', function(data) {
26+
console.log('child: ' + data);
27+
});
28+
29+
child.stderr.on('data', function(data) {
30+
console.log('child err: ' + data);
31+
});
32+
33+
// Crash if Haskell process returned
34+
child.on('exit', function() {
35+
console.log("child returned!");
36+
process.exit(1);
37+
});
38+
39+
console.log('Child is ready');
40+
}
41+
42+
return child;
43+
}
44+
45+
exports.handler = function(event, context, callback) {
46+
47+
// keeping the child (Haskell) process around means that the node event
48+
// loop is never really empty. Setting this means that Lambda won't wait
49+
// around for something that'll never happen.
50+
context.callbackWaitsForEmptyEventLoop = false;
51+
52+
// Get a new response ID and directory
53+
const respId = crypto.randomBytes(20).toString('hex');
54+
const responseDirTemplate = path.join(os.tmpdir(), 'resp-');
55+
56+
fs.mkdtemp(responseDirTemplate, (err, responseDir) => {
57+
58+
if (err) throw err;
59+
60+
const responseFile = path.join(responseDir, 'response.json');
61+
62+
console.log("Expecting answer in " + responseDir);
63+
64+
let fswatcher = null;
65+
let fileWasHandled = false;
66+
67+
fswatcher = fs.watch(responseDir, (eventType, filename) => {
68+
69+
console.log("Got event: " + eventType + " at " + filename);
70+
71+
// my gut feeling is that there could be a race condition if a
72+
// thread yields _after_ the eval of "!fileWasHandled" and _before_
73+
// setting "fileWasHandled = true", though not 100% sure since node
74+
// is single threaded and I have no idea if any thread can just
75+
// "yield"
76+
if (eventType === 'rename' && filename === 'response.json' && !fileWasHandled) {
77+
78+
// prevent other events from trying to handle the file
79+
fileWasHandled = true;
80+
81+
// no need to watch this file anymore
82+
fswatcher.close();
83+
84+
console.log("Event is response");
85+
86+
// Parse the response from the Haskell process
87+
const response = JSON.parse(fs.readFileSync(responseFile, 'utf8'));
88+
console.log("Parsed response: " + JSON.stringify(response));
89+
90+
// Clean up the directory and finally reply
91+
console.log("cleaning up");
92+
fs.unlink(responseFile, (err) => {
93+
if (err) throw err;
94+
fs.rmdir(responseDir);
95+
});
96+
callback(null, response);
97+
}
98+
});
99+
100+
// Send the request to the child
101+
const req = { responseFile: responseFile, request: event }
102+
const reqStr = JSON.stringify(req);
103+
console.log("Writing request: " + reqStr);
104+
105+
getHaskellProcess().stdin.write(reqStr + "\n");
106+
console.log("Request was sent to Haskell process.");
107+
});
108+
}

infra/wai-lambda/nix/packages.nix

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{ haskell
22
, haskellPackages
33
, lib
4+
, runCommand
45
, writeText
6+
, zip
57
}:
68
rec
79
{ wai-lambda-sdist = haskell.lib.sdistTarball wai-lambda;
@@ -14,4 +16,13 @@ rec
1416
"^LICENSE$"
1517
];
1618
wai-lambda-version-file = writeText "version" wai-lambda.version;
19+
wai-lambda-js-wrapper = ../main.js;
20+
wai-lambda-js-build-lambda = exe: runCommand "build-lambda" {}
21+
''
22+
cp ${wai-lambda-js-wrapper} main.js
23+
# Can't be called 'main' otherwise lambda tries to load it
24+
cp ${exe} main_hs
25+
mkdir $out
26+
${zip}/bin/zip -r $out/function.zip main.js main_hs
27+
'';
1728
}

0 commit comments

Comments
 (0)