From 3b42f22a4f36dad0c53bb238e64aada352a063cf Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 7 Dec 2022 21:14:33 +0000 Subject: [PATCH] A couple of fixes to the HTTP API - Flip http.websocket and http.websocketAsync docs (fixes #1244) - Fix http.request queuing a http_failure event with no URL when passing a malformed URL - Fix http.websocketAsync not queuing websocket_failure events on immediate failure. --- .../computercraft/lua/rom/apis/http/http.lua | 44 ++++++++++++------- .../test-rom/spec/apis/http_spec.lua | 26 +++++++++++ .../resources/test-rom/spec/test_helpers.lua | 17 +++++++ 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/http/http.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/http/http.lua index d0d5909ad..8f38c9b64 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/http/http.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/http/http.lua @@ -193,13 +193,15 @@ function request(_url, _post, _headers, _binary) expect(2, _post, "string", "nil") expect(3, _headers, "table", "nil") expect(4, _binary, "boolean", "nil") - url = _url.url + url = _url end local ok, err = nativeHTTPRequest(_url, _post, _headers, _binary) if not ok then os.queueEvent("http_failure", url, err) end + + -- Return true/false for legacy reasons. Undocumented, as it shouldn't be relied on. return ok, err end @@ -257,6 +259,32 @@ end local nativeWebsocket = native.websocket + +--[[- Asynchronously open a websocket. + +This returns immediately, a @{websocket_success} or @{websocket_failure} +will be queued once the request has completed. + +@tparam string url The websocket url to connect to. This should have the +`ws://` or `wss://` protocol. +@tparam[opt] { [string] = string } headers Additional headers to send as part +of the initial websocket connection. +@since 1.80pr1.3 +@changed 1.95.3 Added User-Agent to default headers. +]] +function websocketAsync(url, headers) + expect(1, url, "string") + expect(2, headers, "table", "nil") + + local ok, err = nativeWebsocket(url, headers) + if not ok then + os.queueEvent("websocket_failure", url, err) + end + + -- Return true/false for legacy reasons. Undocumented, as it shouldn't be relied on. + return ok, err +end + --[[- Open a websocket. @tparam string url The websocket url to connect to. This should have the @@ -271,20 +299,6 @@ of the initial websocket connection. @changed 1.80pr1.3 No longer asynchronous. @changed 1.95.3 Added User-Agent to default headers. ]] -websocketAsync = nativeWebsocket - ---[[- Asynchronously open a websocket. - -This returns immediately, a @{websocket_success} or @{websocket_failure} -will be queued once the request has completed. - -@tparam string url The websocket url to connect to. This should have the -`ws://` or `wss://` protocol. -@tparam[opt] { [string] = string } headers Additional headers to send as part -of the initial websocket connection. -@since 1.80pr1.3 -@changed 1.95.3 Added User-Agent to default headers. -]] function websocket(_url, _headers) expect(1, _url, "string") expect(2, _headers, "table", "nil") diff --git a/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua index 4a6a8b803..ca8788c22 100644 --- a/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua @@ -1,3 +1,5 @@ +local timeout = require "test_helpers".timeout + describe("The http library", function() describe("http.checkURL", function() it("accepts well formed domains", function() @@ -18,4 +20,28 @@ describe("The http library", function() expect({ http.checkURL("http://127.0.0.1") }):same({ false, "Domain not permitted" }) end) end) + + describe("http.websocketAsync", function() + it("queues an event for immediate failures", function() + timeout(1, function() + local url = "http://not.a.websocket" + http.websocketAsync(url) + local _, url2, message = os.pullEvent("websocket_failure") + expect(url2):eq(url) + expect(message):eq("Invalid scheme 'http'") + end) + end) + end) + + describe("http.requestAsync", function() + it("queues an event for immediate failures", function() + timeout(1, function() + local url = "ws://not.a.request" + http.request(url) + local _, url2, message = os.pullEvent("http_failure") + expect(url2):eq(url) + expect(message):eq("Invalid protocol 'ws'") + end) + end) + end) end) diff --git a/projects/core/src/test/resources/test-rom/spec/test_helpers.lua b/projects/core/src/test/resources/test-rom/spec/test_helpers.lua index d11a33b3d..7ed9dcad6 100644 --- a/projects/core/src/test/resources/test-rom/spec/test_helpers.lua +++ b/projects/core/src/test/resources/test-rom/spec/test_helpers.lua @@ -70,8 +70,25 @@ local function with_window_lines(width, height, fn) return out end +local function timeout(time, fn) + local timer = os.startTimer(time) + local co = coroutine.create(fn) + local ok, result, event = true, nil, { n = 0 } + while coroutine.status(co) ~= "dead" do + if event[1] == "timer" and event[2] == timer then error("Timeout", 2) end + + if result == nil or event[1] == result or event[1] == "terminated" then + ok, result = coroutine.resume(co, table.unpack(event, 1, event.n)) + if not ok then error(result, 0) end + end + + event = table.pack(coroutine.yield()) + end +end + return { capture_program = capture_program, with_window = with_window, with_window_lines = with_window_lines, + timeout = timeout, }