Skip to content

Commit f9d9c14

Browse files
authored
Merge pull request #4 from revelrylabs/2/chunked-responses
Receive chunked responses to get around max payload limit
2 parents 90adec0 + e5a1f8b commit f9d9c14

File tree

5 files changed

+61
-7
lines changed

5 files changed

+61
-7
lines changed

lib/nodejs/worker.ex

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
defmodule NodeJS.Worker do
22
use GenServer
33

4+
# Port can't do more than this.
5+
@read_chunk_size 65_536
6+
47
@moduledoc """
58
A genserver that controls the starting of the node service
69
"""
@@ -21,15 +24,42 @@ defmodule NodeJS.Worker do
2124
@doc false
2225
def init(module_path) do
2326
node = System.find_executable("node")
24-
port = Port.open({:spawn_executable, node}, env: [{'NODE_PATH', String.to_charlist(module_path)}], args: [node_service_path()])
27+
28+
port =
29+
Port.open(
30+
{:spawn_executable, node},
31+
line: @read_chunk_size,
32+
env: [
33+
{'NODE_PATH', String.to_charlist(module_path)},
34+
{'WRITE_CHUNK_SIZE', String.to_charlist("#{@read_chunk_size}")}
35+
],
36+
args: [node_service_path()]
37+
)
38+
2539
{:ok, [node_service_path(), port]}
2640
end
2741

42+
defp get_response(data \\ '') do
43+
receive do
44+
{_, {:data, {flag, chunk}}} ->
45+
data = data ++ chunk
46+
47+
case flag do
48+
:noeol -> get_response(data)
49+
:eol -> data
50+
end
51+
end
52+
end
53+
2854
@doc false
2955
def handle_call({module, args}, _from, [_, port] = state) when is_tuple(module) do
3056
body = Jason.encode!([Tuple.to_list(module), args])
3157
Port.command(port, "#{body}\n")
32-
response = receive do {_, {:data, data}} -> decode(data) end
58+
59+
response =
60+
get_response()
61+
|> decode()
62+
3363
{:reply, response, state}
3464
end
3565

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule NodeJS.MixProject do
44
def project do
55
[
66
app: :nodejs,
7-
version: "0.2.3",
7+
version: "1.0.0",
88
elixir: "~> 1.6",
99
start_permanent: Mix.env() == :prod,
1010
deps: deps(),

priv/server.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const path = require('path')
22
const readline = require('readline')
33
const { MODULE_SEARCH_PATH } = process.env
4+
const WRITE_CHUNK_SIZE = parseInt(process.env.WRITE_CHUNK_SIZE, 10)
45

56
function rewritePath(oldPath) {
67
return oldPath
@@ -61,9 +62,13 @@ async function getResponse(string) {
6162
}
6263

6364
async function onLine(string) {
64-
const response = await getResponse(string)
65+
const buffer = Buffer.from(`${await getResponse(string)}\n`)
6566

66-
process.stdout.write(response)
67+
for (let i = 0; i < buffer.length; i += WRITE_CHUNK_SIZE) {
68+
let chunk = buffer.slice(i, i + WRITE_CHUNK_SIZE)
69+
70+
process.stdout.write(chunk)
71+
}
6772
}
6873

6974
function startServer() {

test/js/keyed-functions.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ function throwTypeError() {
1616
throw new TypeError('oops')
1717
}
1818

19+
function getBytes(size) {
20+
return Buffer.alloc(size)
21+
}
22+
1923
class Unserializable {
2024
constructor() {
2125
this.circularRef = this
@@ -34,4 +38,13 @@ function getEnv() {
3438
return process.env
3539
}
3640

37-
module.exports = {uuid, hello, math: {add, sub}, throwTypeError, getIncompatibleReturnValue, getArgv, getEnv}
41+
module.exports = {
42+
uuid,
43+
hello,
44+
math: { add, sub },
45+
throwTypeError,
46+
getBytes,
47+
getIncompatibleReturnValue,
48+
getArgv,
49+
getEnv,
50+
}

test/nodejs_test.exs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule NodeJS.Test do
2-
use ExUnit.Case
2+
use ExUnit.Case, async: true
33
doctest NodeJS
44

55
setup_all do
@@ -22,6 +22,12 @@ defmodule NodeJS.Test do
2222
|> String.trim()
2323
end
2424

25+
describe "large payload" do
26+
test "does not explode" do
27+
NodeJS.call!({"keyed-functions", "getBytes"}, [128_000])
28+
end
29+
end
30+
2531
describe "calling default-function-echo" do
2632
test "returns first arg" do
2733
assert 1 == NodeJS.call!("default-function-echo", [1])

0 commit comments

Comments
 (0)