-
Notifications
You must be signed in to change notification settings - Fork 5
/
chttp.cpp
193 lines (159 loc) · 5.22 KB
/
chttp.cpp
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
#include <curl/curl.h>
#include <string>
#include "GarrysMod/Lua/Interface.h"
#include "Logger.h"
#include "RequestWorker.h"
#include "lua.h"
LUA_FUNCTION(lua_run_tasks)
{
RequestWorker::the().run_tasks(LUA);
return 0;
}
/*
* See https://wiki.facepunch.com/gmod/Global.HTTP for documentation.
* The function takes a single table argument, based off the HTTPRequest structure.
* It returns a boolean whether a request was sent or not.
*/
LUA_FUNCTION(CHTTP)
{
if (!LUA->IsType(1, GarrysMod::Lua::Type::Table)) {
return 0;
}
auto request = std::make_shared<HTTPRequest>();
std::string failreason;
// Fetch failed handler
LUA->GetField(1, "failed");
if (LUA->IsType(-1, GarrysMod::Lua::Type::Function)) {
request->failed = std::make_shared<LuaReference>(LUA);
} else {
LUA->Pop();
}
// Fetch method
LUA->GetField(1, "method");
if (LUA->IsType(-1, GarrysMod::Lua::Type::String)) {
request->method = HTTPMethod::from_string(LUA->GetString(-1));
}
LUA->Pop();
// Fetch url
LUA->GetField(1, "url");
if (LUA->IsType(-1, GarrysMod::Lua::Type::String)) {
request->url = LUA->GetString(-1);
} else {
failreason = "invalid url";
goto exit;
}
LUA->Pop();
// Fetch success handler
LUA->GetField(1, "success");
if (LUA->IsType(-1, GarrysMod::Lua::Type::Function)) {
request->success = std::make_shared<LuaReference>(LUA);
} else {
LUA->Pop();
}
// Fetch headers
LUA->GetField(1, "headers");
if (LUA->IsType(-1, GarrysMod::Lua::Type::Table)) {
lua_table_to_map(LUA, -1, request->headers);
}
LUA->Pop();
// Fetch parameters
LUA->GetField(1, "parameters");
if (LUA->IsType(-1, GarrysMod::Lua::Type::Table)) {
lua_table_to_map(LUA, -1, request->parameters);
}
LUA->Pop();
// Fetch type
LUA->GetField(1, "type");
if (LUA->IsType(-1, GarrysMod::Lua::Type::String)) {
request->type = LUA->GetString(-1);
}
LUA->Pop();
// Fetch body
LUA->GetField(1, "body");
if (LUA->IsType(-1, GarrysMod::Lua::Type::String)) {
unsigned int body_length;
char const* body = LUA->GetString(-1, &body_length);
request->body = std::string(body, body_length);
}
LUA->Pop();
// Fetch timeout
LUA->GetField(1, "timeout");
if (LUA->IsType(-1, GarrysMod::Lua::Type::Number)) {
request->timeout = (long)LUA->GetNumber(-1);
}
LUA->Pop();
if (!getenv("CHTTP_FORCE_HOOK")) {
// If we are using timers, ensure that the timer is still present.
// It might have gotten destroyed if there was an exception while running a callback.
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
LUA->GetField(-1, "timer");
LUA->GetField(-1, "Exists");
LUA->PushString("__chttpThinkTimer");
LUA->Call(1, 1);
auto exists = LUA->GetBool(-1);
LUA->Pop();
LUA->Pop();
LUA->Pop();
if (!exists) {
Logger::msg("Recreating timer for processing requests...");
register_zero_delay_timer(LUA, "__chttpThinkTimer", lua_run_tasks);
}
}
RequestWorker::the().requests().push(request);
exit:
if (!failreason.empty()) {
RequestWorker::the().tasks().push(std::make_shared<FailCallbackTask>(request->failed, failreason));
}
LUA->PushBool(true); // Push result to the stack
return 1; // We are returning a single value
}
GMOD_MODULE_OPEN()
{
// Set up logging
if (!Logger::init()) {
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
LUA->GetField(-1, "print");
LUA->PushString(("[chttp] Could not get all required logging functions. Most messages will not be shown."));
LUA->Call(1, 0);
LUA->Pop();
}
// Initialize curl
curl_global_init(CURL_GLOBAL_ALL);
// We are working on the global table today
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
// Push the function mapping (first is the key/function name,
// second is the value/actual function)
LUA->PushString("CHTTP");
LUA->PushCFunction(CHTTP);
// SetTable takes the item at the top of the stack (value) and
// the second item from the top (key) and adds them to the table
// at the stack offset mentioned in the parameter (again, -1 is the top)
LUA->SetTable(-3);
LUA->PushString("CHTTP_VERSION");
LUA->PushString(CHTTP_VERSION);
LUA->SetTable(-3);
// Pop the global table from the stack again
LUA->Pop();
if (getenv("CHTTP_FORCE_HOOK")) {
Logger::msg("Processing requests using a hook...");
register_hook(LUA, "Think", "__chttpThinkHook", lua_run_tasks);
} else {
Logger::msg("Processing requests using a zero-delay timer...");
register_zero_delay_timer(LUA, "__chttpThinkTimer", lua_run_tasks);
}
// Start the background thread
RequestWorker::the();
return 0;
}
GMOD_MODULE_CLOSE()
{
// Process remaining requests and callbacks
while (RequestWorker::the().has_work()) {
RequestWorker::the().run_tasks(LUA);
}
// Stop the request worker
RequestWorker::the().stop();
// Cleanup curl
curl_global_cleanup();
return 0;
}