diff --git a/Capy64/Assets/Lua/boot/04_http.lua b/Capy64/Assets/Lua/boot/04_http.lua index 020a900..faffa58 100644 --- a/Capy64/Assets/Lua/boot/04_http.lua +++ b/Capy64/Assets/Lua/boot/04_http.lua @@ -2,6 +2,24 @@ local http = require("http") local event = require("event") local expect = require("expect").expect +local WebSocketHandle = {} +function WebSocketHandle:close() + self:closeAsync() + local _, id + repeat + _, id = event.pull("websocket_close") + until id == self.requestId +end + +function WebSocketHandle:receive() + local _, id, par + repeat + _, id, par = event.pull("websocket_message") + until id == self.requestId + + return par +end + function http.request(url, body, headers, options) expect(1, url, "string") expect(2, body, "string", "nil") @@ -37,7 +55,38 @@ function http.post(url, body, headers, options) expect(1, url, "string") expect(2, body, "string", "nil") expect(3, headers, "table", "nil") - expect(4, options, "table", "nil") + expect(4, options, "table", "nil") return http.request(url, body, headers, options) -end \ No newline at end of file +end + +local function buildWebsocketHandle(requestId, handle) + handle.requestId = requestId + local metatable = getmetatable(handle) or {} + metatable.__index = WebSocketHandle + + setmetatable(handle, metatable) + + return handle +end + +function http.websocket(url, headers) + expect(1, url, "string") + expect(2, headers, "table", "nil") + + if not http.checkURL(url) then + return nil, "Invalid URL" + end + + local requestId = http.websocketAsync(url, headers) + local ev, id, par + repeat + ev, id, par = event.pull("websocket_connect", "websocket_failure") + until id == requestId + + if ev == "http_failure" then + return nil, par + end + + return buildWebsocketHandle(requestId, par) +end diff --git a/Capy64/LuaRuntime/Handlers/WebSocketHandle.cs b/Capy64/LuaRuntime/Handlers/WebSocketHandle.cs index 8307105..9c95721 100644 --- a/Capy64/LuaRuntime/Handlers/WebSocketHandle.cs +++ b/Capy64/LuaRuntime/Handlers/WebSocketHandle.cs @@ -56,14 +56,14 @@ class WebSocketHandle : IHandle { var L = Lua.FromIntPtr(state); - var data = L.CheckString(2); + var data = L.CheckBuffer(2); var h = GetHandle(L, false); if (h is null || h._client.State == WebSocketState.Closed) L.Error("connection is closed"); - h._client.SendAsync(Encoding.ASCII.GetBytes(data), WebSocketMessageType.Text, true, CancellationToken.None); + h._client.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None); return 0; } diff --git a/Capy64/LuaRuntime/Libraries/HTTP.cs b/Capy64/LuaRuntime/Libraries/HTTP.cs index 844434c..d0c0bc1 100644 --- a/Capy64/LuaRuntime/Libraries/HTTP.cs +++ b/Capy64/LuaRuntime/Libraries/HTTP.cs @@ -10,8 +10,6 @@ using System.Net.Http; using System.Net.WebSockets; using System.Text; using System.Threading; -using System.Threading.Tasks; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Capy64.LuaRuntime.Libraries; #nullable enable @@ -21,6 +19,8 @@ public class HTTP : IPlugin private static HttpClient _httpClient; private static long _requestId; + public static readonly string UserAgent = $"Capy64/{Capy64.Version}"; + private readonly IConfiguration _configuration; private readonly LuaRegister[] HttpLib = new LuaRegister[] { @@ -46,7 +46,7 @@ public class HTTP : IPlugin _game = game; _requestId = 0; _httpClient = new(); - _httpClient.DefaultRequestHeaders.Add("User-Agent", $"Capy64/{Capy64.Version}"); + _httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent); _configuration = configuration; } @@ -261,6 +261,41 @@ public class HTTP : IPlugin var requestId = _requestId++; var wsClient = new ClientWebSocket(); + + wsClient.Options.SetRequestHeader("User-Agent", UserAgent); + + if (L.IsTable(2)) // headers + { + L.PushCopy(2); + L.PushNil(); + + while (L.Next(-2)) + { + L.PushCopy(-2); + + var k = L.CheckString(-1); + if (L.IsStringOrNumber(-2)) + { + var v = L.ToString(-2); + + wsClient.Options.SetRequestHeader(k, v); + } + else if (L.IsNil(-2)) + { + wsClient.Options.SetRequestHeader(k, null); + } + else + { + L.ArgumentError(3, "string, number or nil expected, got " + L.TypeName(L.Type(-2)) + " in field " + k); + } + + L.Pop(2); + } + + L.Pop(1); + } + + var connectTask = wsClient.ConnectAsync(uri, CancellationToken.None); connectTask.ContinueWith(async task => { @@ -301,7 +336,7 @@ public class HTTP : IPlugin builder.Append(data); } - if(result.EndOfMessage) + if (result.EndOfMessage) { _game.LuaRuntime.PushEvent("websocket_message", requestId, builder.ToString()); builder.Clear();