-
Notifications
You must be signed in to change notification settings - Fork 1
/
gm_webapi_socket.lua
215 lines (168 loc) · 6.2 KB
/
gm_webapi_socket.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
--[[
This is required because SOMEONE on the gmod dev team decided to remove how we can interact with sockets clientside using HTTP or dhtml panels. Thanks bro, real cool!
This will likely need work done before it is in a production usable state!
For now it is for testing only. Hopefully this can be used to serve as a socket based alternative to HTTP for the library
This has to use a c++ module clientside. So the chances of this being used is not very likely for most clients. But if the module is available, we could in theory use it.
If not, we can default back to the version of the library that uses HTTP instead. Once this is made to be just as usable I will work on making three versions of the library:
Stand Alone HTTP Only(will not require players to install bromsock)
Stand Alone Socket Only(Will require all players to have bromsock on their client)
Hybrid prioritizing Socket(If bromsock is on the client we will use it, if not then we will use HTTP)
]]
require( "bromsock" )
local webAPIConn = BromSock()
--Localizations
local isstring = isstring
local isnumber = isnumber
local istable = istable
local isfunction = isfunction
local ErrorNoHalt = ErrorNoHalt
local apiUrl = ""
local methods = {}
--[[
Doccumentation: https://github.com/The-Lord-of-Owls/gm_webapi/tree/main#apiadd-id-route-cback-onerror-cachettl-customheaders-
]]
local function Add( id, route, cback, onError, cacheTTL )
--Check if an id was provided
if not id or not isstring( id ) then
return ErrorNoHalt( "API ERROR: You must provide an id when setting up a new API method!" )
end
--Check if a route was provided
if not route or not isstring( route ) then
return ErrorNoHalt( "API ERROR: You must specify a route when setting up a new API method!" )
end
--Check if a callback function was provided
if not cback or not isfunction( cback ) then
return ErrorNoHalt( "API ERROR: You must provide a callback function when setting up a new API method!" )
end
--Setup method object
local method = {
route = route,
method.cback = cback
}
--Optional error response
if onError and isfunction( onError ) then
method.onError = onError
elseif onError then
ErrorNoHalt( "API WARNING: Custom onError must be a function, the API method has been created without custom error handling!" )
end
--Handle if we will cache the value
if cacheTTL and isnumber( cacheTTL ) then
method.cacheRes = ""
method.cacheTTL = cacheTTL
method.lastCache = 0
elseif cacheTTL then
ErrorNoHalt( "API WARNING: cacheTTL for API methods must be a number value, the API method has been created with caching disabled!" )
end
--Make the method object accessible
methods[ id ] = method
end
--[[
Doccumentation: https://github.com/The-Lord-of-Owls/gm_webapi/tree/main#apiremove-id-
]]
local function Remove( id )
if not id or not isstring( id ) then
return ErrorNoHalt( "API WARNING: Please provide a id string for the method you want to remove!" )
end
methods[ id ] = nil
end
--[[
Doccumentation: https://github.com/The-Lord-of-Owls/gm_webapi/tree/main#apicall-id-params-
]]
local function Call( id, params )
--Check if the API url has been set
if webAPIConn:IsValid() then
return ErrorNoHalt( "API ERROR: API socket has not been initialized, aborting API call! Please make sure to run api.Init( url, port ) before calling any methods!" )
end
--Check if the API method exists
if not methods[ id ] then
return ErrorNoHalt( "API ERROR: The API method '" .. id .. "' does not exist!" )
end
--The API method
local apiMethod = methods[ id ]
--Check if the API has a proper callback function
if not apiMethod.cback then
return ErrorNoHalt( "API ERROR: The API method '" .. id .. "' does not have a callback function!")
end
--Check if should return cached value
if apiMethod.cacheTTL and apiMethod.cacheTTL < CurTime() then
apiMethod.cback( method.cacheRes )
return
end
--Handle API calls with socket connection
local packet = BromPacket()
packet:WriteLine( "Route: " .. apiMethod.route )
packet:WriteLine( util.TableToJSON( params ) or "{}" )
packet:WriteLine( "" )
webAPIConn:Send( packet, true )
end
--[[
Doccumentation: https://github.com/The-Lord-of-Owls/gm_webapi/tree/main#apigettable
]]
local function GetTable()
return methods or {}
end
--[[
Initializes connection
]]
local function Init( url, port, timeout, ssl )
if not url and isstring( url ) then
return ErrorNoHalt( "API ERROR: Please provide a valid url string for the API!" )
end
if not port and isnumber( port ) then
return ErrorNoHalt( "API ERROR: Please provide a valid port number for the API!" )
end
--Handling connection successfull and SSL
webAPIConn:SetCallbackConnect( function( _, connected, ip, port )
if not connected then
return ErrorNoHalt( "API ERROR: Unable to successfully connect to the API socket!")
end
--Switch to SSL
if ssl then webAPIConn:StartSSLClient() end
end)
--Handling Response from API
webAPIConn:SetCallbackReceive( function( _, packetIncoming )
local response = packetIncoming:ReadStringAll():Trim()
packetIncoming = nil
webAPIConn:Receive( #response )
local responseData = util.JSONToTable( response ) or {}
if not responseData.id then
return ErrorNoHalt( "API ERROR: The API did not include the id! Aborting callback handling" )
end
local apiMethod = methods[ responseData.id ]
if not apiMethod.cback then
return ErrorNoHalt( "API ERROR: The API method '" .. responseData.id .. "' does not have a callback function!")
end
--Update the cache
if apiMethod.cacheTTL then
apiMethod.lastCache = CurTime() + apiMethod.cacheTTL
apiMethod.cacheRes = res
end
--Run the callback
apiMethod.cback( responseData.resParams )
end)
--Initialize the connection
apiUrl = url
webAPIConn:Connect( url, port )
--Set timeoute in seconds
if timeoute and isnumber( timeoute ) then
webAPIConn:SetTimeout( timeoute * 1000 )
else
webAPIConn:SetTimeout( 60 * 1000 )
end
end
--[[
Setting up our API library table
Doccumentation: https://github.com/The-Lord-of-Owls/gm_webapi/blob/main/README.md#apicall-id-params-
]]
api = setmetatable( {
Add = Add,
Remove = Remove,
Call = Call,
GetTable = GetTable,
Init = Init
}, {
__metatable = "WebAPI Handler",
__call = function( self, ... )
Call( ... )
end
} )