http.get is now operational
This commit is contained in:
@@ -82,7 +82,7 @@ std::string errors_string(bool lastonly) {
|
||||
} else {
|
||||
err = err + ", " + reason;
|
||||
}
|
||||
if (data != nullptr) {
|
||||
if ((data != nullptr) && (data[0] != 0)) {
|
||||
err = err + " " + data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,21 +186,21 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
HttpOutRequest::HttpOutRequest() {
|
||||
verify_certificate_ = true;
|
||||
HttpClientRequest::HttpClientRequest() {
|
||||
verify_certificate_ = false;
|
||||
port_ = 0;
|
||||
request_id_ = 0;
|
||||
place_id_ = 0;
|
||||
thread_id_ = 0;
|
||||
}
|
||||
|
||||
void HttpOutRequest::fail(string_view s) {
|
||||
void HttpClientRequest::fail(string_view s) {
|
||||
if (error_.empty()) {
|
||||
error_ = s;
|
||||
}
|
||||
}
|
||||
|
||||
eng::string HttpOutRequest::target() const {
|
||||
eng::string HttpClientRequest::target() const {
|
||||
assert(check().empty());
|
||||
eng::ostringstream oss;
|
||||
oss << (verify_certificate_ ? "cert" : "nocert");
|
||||
@@ -208,11 +208,11 @@ eng::string HttpOutRequest::target() const {
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_verify_certificate(bool flag) {
|
||||
void HttpClientRequest::set_verify_certificate(bool flag) {
|
||||
verify_certificate_ = flag;
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_method(const eng::string &s) {
|
||||
void HttpClientRequest::set_method(const eng::string &s) {
|
||||
eng::string method = util::ascii_toupper(s);
|
||||
if ((method != "GET") && (method != "HEAD")) {
|
||||
fail(util::ss("HTTP method not implemented: ", method, ".",
|
||||
@@ -226,7 +226,7 @@ void HttpOutRequest::set_method(const eng::string &s) {
|
||||
method_ = method;
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_host(const eng::string &s) {
|
||||
void HttpClientRequest::set_host(const eng::string &s) {
|
||||
eng::string host = util::ascii_tolower(s);
|
||||
if (host.empty()) {
|
||||
fail(util::ss("HTTP hostname cannot be empty string."));
|
||||
@@ -247,7 +247,7 @@ void HttpOutRequest::set_host(const eng::string &s) {
|
||||
host_ = host;
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_port(int port) {
|
||||
void HttpClientRequest::set_port(int port) {
|
||||
if ((port < 1) || (port > 65535)) {
|
||||
fail(util::ss("HTTP port must be between 1 and 65535: ", port));
|
||||
return;
|
||||
@@ -259,7 +259,7 @@ void HttpOutRequest::set_port(int port) {
|
||||
port_ = port;
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_path(string_view path) {
|
||||
void HttpClientRequest::set_path(string_view path) {
|
||||
if (!sv::has_prefix(path, "/")) {
|
||||
fail(util::ss("HTTP path must start with slash"));
|
||||
return;
|
||||
@@ -271,7 +271,7 @@ void HttpOutRequest::set_path(string_view path) {
|
||||
path_ = path;
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_param(const eng::string &key, const eng::string &val) {
|
||||
void HttpClientRequest::set_param(const eng::string &key, const eng::string &val) {
|
||||
if (params_.find(key) != params_.end()) {
|
||||
fail(util::ss("HTTP url parameter specified twice: ", key));
|
||||
return;
|
||||
@@ -283,7 +283,7 @@ void HttpOutRequest::set_param(const eng::string &key, const eng::string &val) {
|
||||
params_[key] = val;
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_url(string_view url) {
|
||||
void HttpClientRequest::set_url(string_view url) {
|
||||
ParsedURL parsed_url(url);
|
||||
if (!parsed_url.valid) {
|
||||
fail(util::ss("syntactically invalid URL: ", url));
|
||||
@@ -301,7 +301,7 @@ void HttpOutRequest::set_url(string_view url) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_verify_certificate(LuaStack &LS, LuaSlot val) {
|
||||
void HttpClientRequest::set_verify_certificate(LuaStack &LS, LuaSlot val) {
|
||||
if (!LS.isboolean(val)) {
|
||||
fail(util::ss("HTTP verify_certificate must be a boolean"));
|
||||
return;
|
||||
@@ -309,7 +309,7 @@ void HttpOutRequest::set_verify_certificate(LuaStack &LS, LuaSlot val) {
|
||||
set_verify_certificate(LS.ckboolean(val));
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_method(LuaStack &LS, LuaSlot val) {
|
||||
void HttpClientRequest::set_method(LuaStack &LS, LuaSlot val) {
|
||||
if (!LS.isstring(val)) {
|
||||
fail(util::ss("HTTP method must be a string"));
|
||||
return;
|
||||
@@ -317,7 +317,7 @@ void HttpOutRequest::set_method(LuaStack &LS, LuaSlot val) {
|
||||
set_method(LS.ckstring(val));
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_host(LuaStack &LS, LuaSlot val) {
|
||||
void HttpClientRequest::set_host(LuaStack &LS, LuaSlot val) {
|
||||
if (!LS.isstring(val)) {
|
||||
fail(util::ss("HTTP host must be a string"));
|
||||
return;
|
||||
@@ -325,7 +325,7 @@ void HttpOutRequest::set_host(LuaStack &LS, LuaSlot val) {
|
||||
set_host(LS.ckstring(val));
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_port(LuaStack &LS, LuaSlot val) {
|
||||
void HttpClientRequest::set_port(LuaStack &LS, LuaSlot val) {
|
||||
if (!LS.isint(val)) {
|
||||
fail(util::ss("HTTP port must be an int"));
|
||||
return;
|
||||
@@ -333,7 +333,7 @@ void HttpOutRequest::set_port(LuaStack &LS, LuaSlot val) {
|
||||
set_port(LS.ckint(val));
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_path(LuaStack &LS, LuaSlot val) {
|
||||
void HttpClientRequest::set_path(LuaStack &LS, LuaSlot val) {
|
||||
if (!LS.isstring(val)) {
|
||||
fail(util::ss("HTTP path must be a string"));
|
||||
return;
|
||||
@@ -341,7 +341,7 @@ void HttpOutRequest::set_path(LuaStack &LS, LuaSlot val) {
|
||||
set_path(LS.ckstring(val));
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_param(LuaStack &LS, LuaSlot key, LuaSlot val) {
|
||||
void HttpClientRequest::set_param(LuaStack &LS, LuaSlot key, LuaSlot val) {
|
||||
if (!LS.isstring(key)) {
|
||||
fail(util::ss("HTTP url parameter key must be a string"));
|
||||
return;
|
||||
@@ -353,7 +353,7 @@ void HttpOutRequest::set_param(LuaStack &LS, LuaSlot key, LuaSlot val) {
|
||||
set_param(LS.ckstring(key), LS.ckstring(val));
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_params(LuaStack &LS0, LuaSlot tab) {
|
||||
void HttpClientRequest::set_params(LuaStack &LS0, LuaSlot tab) {
|
||||
if (!LS0.istable(tab)) {
|
||||
fail(util::ss("HTTP params must be a table"));
|
||||
return;
|
||||
@@ -366,7 +366,7 @@ void HttpOutRequest::set_params(LuaStack &LS0, LuaSlot tab) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_url(LuaStack &LS, LuaSlot val) {
|
||||
void HttpClientRequest::set_url(LuaStack &LS, LuaSlot val) {
|
||||
if (!LS.isstring(val)) {
|
||||
fail(util::ss("HTTP url must be a string"));
|
||||
return;
|
||||
@@ -374,7 +374,7 @@ void HttpOutRequest::set_url(LuaStack &LS, LuaSlot val) {
|
||||
set_url(LS.ckstring(val));
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_defaults() {
|
||||
void HttpClientRequest::set_defaults() {
|
||||
if (method_.empty()) {
|
||||
method_ = "GET";
|
||||
}
|
||||
@@ -383,7 +383,7 @@ void HttpOutRequest::set_defaults() {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpOutRequest::set_config(LuaStack &LS0, LuaSlot tab) {
|
||||
void HttpClientRequest::set_config(LuaStack &LS0, LuaSlot tab) {
|
||||
LuaVar key, val;
|
||||
LuaStack LS(LS0.state(), key, val);
|
||||
LS.set(key, LuaNil);
|
||||
@@ -412,7 +412,7 @@ void HttpOutRequest::set_config(LuaStack &LS0, LuaSlot tab) {
|
||||
}
|
||||
}
|
||||
|
||||
eng::string HttpOutRequest::check() const {
|
||||
eng::string HttpClientRequest::check() const {
|
||||
if (!error_.empty()) {
|
||||
return error_;
|
||||
}
|
||||
@@ -432,7 +432,7 @@ eng::string HttpOutRequest::check() const {
|
||||
}
|
||||
|
||||
|
||||
void HttpOutRequest::send_internal(StreamBuffer *sb, bool debug_string) const {
|
||||
void HttpClientRequest::send_internal(StreamBuffer *sb, bool debug_string) const {
|
||||
// If there's an error in the request, handle it. In debug string mode,
|
||||
// we just put the error into the output. In production mode, we assert
|
||||
// fail.
|
||||
@@ -484,7 +484,7 @@ void HttpOutRequest::send_internal(StreamBuffer *sb, bool debug_string) const {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpOutRequest::serialize(StreamBuffer *sb) const {
|
||||
void HttpClientRequest::serialize(StreamBuffer *sb) const {
|
||||
sb->write_int64(request_id_);
|
||||
sb->write_int64(place_id_);
|
||||
sb->write_int64(thread_id_);
|
||||
@@ -501,7 +501,7 @@ void HttpOutRequest::serialize(StreamBuffer *sb) const {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpOutRequest::deserialize(StreamBuffer *sb) {
|
||||
void HttpClientRequest::deserialize(StreamBuffer *sb) {
|
||||
request_id_ = sb->read_int64();
|
||||
place_id_ = sb->read_int64();
|
||||
thread_id_ = sb->read_int64();
|
||||
@@ -520,39 +520,40 @@ void HttpOutRequest::deserialize(StreamBuffer *sb) {
|
||||
}
|
||||
}
|
||||
|
||||
eng::string HttpOutRequest::DebugString() {
|
||||
eng::string HttpClientRequest::DebugString() {
|
||||
StreamBuffer sb;
|
||||
send_internal(&sb, true);
|
||||
return eng::string(sb.view());
|
||||
}
|
||||
|
||||
void HttpOutRequestMap::serialize(StreamBuffer *sb) const {
|
||||
void HttpClientRequestMap::serialize(StreamBuffer *sb) const {
|
||||
sb->write_int32(size());
|
||||
for (const auto &pair : *this) {
|
||||
pair.second.serialize(sb);
|
||||
}
|
||||
}
|
||||
|
||||
void HttpOutRequestMap::deserialize(StreamBuffer *sb) {
|
||||
void HttpClientRequestMap::deserialize(StreamBuffer *sb) {
|
||||
int32_t count = sb->read_int32();
|
||||
clear();
|
||||
HttpOutRequest req;
|
||||
HttpClientRequest req;
|
||||
for (int i = 0; i < count; i++) {
|
||||
req.deserialize(sb);
|
||||
(*this)[req.request_id()] = req;
|
||||
}
|
||||
}
|
||||
|
||||
HttpInResponse::HttpInResponse() {
|
||||
HttpClientResponse::HttpClientResponse() {
|
||||
request_id_ = 0;
|
||||
status_code_ = 0;
|
||||
response_length_ = 0;
|
||||
mime_type_ = "";
|
||||
content_length_ = -1;
|
||||
}
|
||||
|
||||
eng::string HttpInResponse::DebugString() const {
|
||||
eng::string HttpClientResponse::DebugString() const {
|
||||
eng::ostringstream oss;
|
||||
oss << "HttpInResponse:" << std::endl;
|
||||
oss << "HttpClientResponse:" << std::endl;
|
||||
oss << " status_code: " << status_code_ << std::endl;
|
||||
oss << " error: " << error_ << std::endl;
|
||||
oss << " content_length: " << content_length_ << std::endl;
|
||||
@@ -565,7 +566,7 @@ eng::string HttpInResponse::DebugString() const {
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void HttpInResponse::fail(int code, string_view message) {
|
||||
void HttpClientResponse::fail(int code, string_view message) {
|
||||
status_code_ = code;
|
||||
error_ = message;
|
||||
mime_type_ = "";
|
||||
@@ -573,7 +574,7 @@ void HttpInResponse::fail(int code, string_view message) {
|
||||
content_ = "";
|
||||
}
|
||||
|
||||
void HttpInResponse::incomplete(bool closed) {
|
||||
void HttpClientResponse::incomplete(bool closed) {
|
||||
if (closed) {
|
||||
fail(500, "internal server error: response truncated");
|
||||
} else {
|
||||
@@ -581,11 +582,11 @@ void HttpInResponse::incomplete(bool closed) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpInResponse::parse_content_encoding(string_view value) {
|
||||
void HttpClientResponse::parse_content_encoding(string_view value) {
|
||||
content_encoding_ = util::ascii_tolower(value);
|
||||
}
|
||||
|
||||
void HttpInResponse::parse_content_length(string_view value) {
|
||||
void HttpClientResponse::parse_content_length(string_view value) {
|
||||
int64_t code = sv::to_int64(value);
|
||||
if ((code < 0) || (code > INT_MAX)) {
|
||||
fail(500, util::ss("internal server error: unparseable content-length: ", value));
|
||||
@@ -593,7 +594,7 @@ void HttpInResponse::parse_content_length(string_view value) {
|
||||
content_length_ = code;
|
||||
}
|
||||
|
||||
void HttpInResponse::parse_content_type(string_view value) {
|
||||
void HttpClientResponse::parse_content_type(string_view value) {
|
||||
eng::string ctype = util::ascii_tolower(value);
|
||||
string_view ctview(ctype);
|
||||
mime_type_ = sv::trim(sv::read_to_sep(ctview, ';'));
|
||||
@@ -613,15 +614,15 @@ void HttpInResponse::parse_content_type(string_view value) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpInResponse::parse_location(string_view value) {
|
||||
void HttpClientResponse::parse_location(string_view value) {
|
||||
location_ = url_decode(value);
|
||||
}
|
||||
|
||||
void HttpInResponse::parse_transfer_encoding(string_view value) {
|
||||
void HttpClientResponse::parse_transfer_encoding(string_view value) {
|
||||
transfer_encoding_ = util::ascii_tolower(value);
|
||||
}
|
||||
|
||||
void HttpInResponse::parse_header(string_view header, string_view value) {
|
||||
void HttpClientResponse::parse_header(string_view header, string_view value) {
|
||||
if (header == "content-encoding") {
|
||||
parse_content_encoding(value);
|
||||
} else if (header == "content-length") {
|
||||
@@ -637,7 +638,7 @@ void HttpInResponse::parse_header(string_view header, string_view value) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpInResponse::parse_content_basic(std::string_view &view, bool closed) {
|
||||
bool HttpClientResponse::parse_content_basic(std::string_view &view, bool closed) {
|
||||
if (content_length_ >= 0) {
|
||||
if (content_length_ > MAX_CONTENT_LENGTH) {
|
||||
fail(413, util::ss("payload too large: luprex limit=", MAX_CONTENT_LENGTH));
|
||||
@@ -662,7 +663,7 @@ bool HttpInResponse::parse_content_basic(std::string_view &view, bool closed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpInResponse::parse_content_chunked(std::string_view &view, bool closed) {
|
||||
bool HttpClientResponse::parse_content_chunked(std::string_view &view, bool closed) {
|
||||
int64_t total_size = 0;
|
||||
std::vector<string_view> chunks;
|
||||
while (true) {
|
||||
@@ -711,7 +712,7 @@ bool HttpInResponse::parse_content_chunked(std::string_view &view, bool closed)
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpInResponse::parse(const StreamBuffer *sb, bool closed) {
|
||||
void HttpClientResponse::parse(const StreamBuffer *sb, bool closed) {
|
||||
// We're not going to modify the StreamBuffer at all.
|
||||
// Instead, we work entirely on a view.
|
||||
string_view view = sb->view();
|
||||
@@ -724,19 +725,26 @@ void HttpInResponse::parse(const StreamBuffer *sb, bool closed) {
|
||||
}
|
||||
|
||||
// Parse the status line.
|
||||
string_view protoversion = sv::read_to_space(status);
|
||||
if (!sv::has_prefix(protoversion, "HTTP/")) {
|
||||
fail(500, util::ss("internal server error: status line appears corrupt"));
|
||||
return;
|
||||
}
|
||||
string_view scode = sv::read_to_space(status);
|
||||
int64_t code = sv::to_int64(scode, 0);
|
||||
if ((code < 100) || (code > 599)) {
|
||||
fail(500, util::ss("internal server error: invalid response code: ", scode));
|
||||
return;
|
||||
}
|
||||
status_code_ = code;
|
||||
|
||||
// Responses outside the range 200-299 are errors,
|
||||
// and therefore must store an error message.
|
||||
if ((code < 200) || (code > 299)) {
|
||||
error_ = status;
|
||||
if (error_.empty()) {
|
||||
fail(code, util::ss("error code ", code));
|
||||
if (status.empty()) {
|
||||
error_ = util::ss("error code ", code);
|
||||
} else {
|
||||
error_ = status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,7 +837,7 @@ void HttpInResponse::parse(const StreamBuffer *sb, bool closed) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpInResponse::store(LuaStack &LS0, LuaSlot tab) const {
|
||||
void HttpClientResponse::store(LuaStack &LS0, LuaSlot tab) const {
|
||||
LuaStack LS(LS0.state());
|
||||
|
||||
LS.newtable(tab);
|
||||
@@ -860,8 +868,8 @@ void HttpInResponse::store(LuaStack &LS0, LuaSlot tab) const {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpInResponse::store_fail(LuaStack &LS, LuaSlot tab, int status_code, std::string_view error) {
|
||||
HttpInResponse response;
|
||||
void HttpClientResponse::store_fail(LuaStack &LS, LuaSlot tab, int status_code, std::string_view error) {
|
||||
HttpClientResponse response;
|
||||
response.fail(status_code, error);
|
||||
response.store(LS, tab);
|
||||
}
|
||||
@@ -912,7 +920,7 @@ LuaDefine(http_request, "request",
|
||||
LuaArg tab;
|
||||
LuaRet str;
|
||||
LuaStack LS(L, tab, str);
|
||||
HttpOutRequest req;
|
||||
HttpClientRequest req;
|
||||
req.set_config(LS, tab);
|
||||
req.set_defaults();
|
||||
eng::string error = req.check();
|
||||
@@ -969,7 +977,7 @@ LuaDefine(http_response, "response",
|
||||
LuaArg text;
|
||||
LuaRet tab;
|
||||
LuaStack LS(L, text, tab);
|
||||
HttpInResponse resp;
|
||||
HttpClientResponse resp;
|
||||
StreamBuffer sb;
|
||||
sb.write_bytes(LS.ckstring(text));
|
||||
resp.parse(&sb, true);
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
#include "wrap-map.hpp"
|
||||
#include "luastack.hpp"
|
||||
#include "streambuffer.hpp"
|
||||
#include "drivenengine.hpp"
|
||||
|
||||
using UrlParameters = eng::map<eng::string, eng::string>;
|
||||
|
||||
class HttpOutRequest : public eng::nevernew {
|
||||
class HttpClientRequest : public eng::nevernew {
|
||||
private:
|
||||
// Request IDs.
|
||||
int64_t request_id_;
|
||||
@@ -59,14 +60,10 @@ private:
|
||||
public:
|
||||
// Construct an empty HTTP request.
|
||||
// All of the fields have empty values.
|
||||
HttpOutRequest();
|
||||
|
||||
// Get the request IDs.
|
||||
int64_t request_id() const { return request_id_; }
|
||||
int64_t place_id() const { return place_id_; }
|
||||
int64_t thread_id() const { return thread_id_; }
|
||||
HttpClientRequest();
|
||||
|
||||
// Get request-related fields.
|
||||
//
|
||||
const eng::string &error() const { return error_; }
|
||||
bool verify_certificate() const { return verify_certificate_; }
|
||||
const eng::string &method() const { return method_; }
|
||||
@@ -74,20 +71,13 @@ public:
|
||||
int port() const { return port_; }
|
||||
const eng::string &path() const { return path_; }
|
||||
|
||||
// Get the network target, eg, "cert:host:port"
|
||||
eng::string target() const;
|
||||
|
||||
// Set the request IDs.
|
||||
void set_request_id(int64_t request_id) { request_id_ = request_id; }
|
||||
void set_place_id(int64_t place_id) { place_id_ = place_id; }
|
||||
void set_thread_id(int64_t thread_id) { thread_id_ = thread_id; }
|
||||
|
||||
// Populate an HTTP request a piece at a time.
|
||||
// Populate an request-related fields one piece at a time.
|
||||
// If you pass an invalid value, or if the field is
|
||||
// already set, the routine will generate an error message
|
||||
// and store it in the error field. In that case, the set
|
||||
// will not happen. If there's already an error in the error
|
||||
// field, it will not be overwritten.
|
||||
//
|
||||
void set_verify_certificate(bool flag);
|
||||
void set_method(const eng::string &method);
|
||||
void set_host(const eng::string &host);
|
||||
@@ -96,7 +86,6 @@ public:
|
||||
void set_param(const eng::string &key, const eng::string &value);
|
||||
void set_url(std::string_view url);
|
||||
|
||||
// Same as above, but using Lua values.
|
||||
void set_verify_certificate(LuaStack &LS, LuaSlot val);
|
||||
void set_method(LuaStack &LS, LuaSlot val);
|
||||
void set_host(LuaStack &LS, LuaSlot val);
|
||||
@@ -106,18 +95,29 @@ public:
|
||||
void set_params(LuaStack &LS, LuaSlot tab);
|
||||
void set_url(LuaStack &LS, LuaSlot val);
|
||||
|
||||
// Set default values for any fields that should have
|
||||
// defaults. This must be done after setting regular
|
||||
// values.
|
||||
// Set default values for method and port.
|
||||
// This must be done after setting regular values.
|
||||
void set_defaults();
|
||||
|
||||
// Populate request-related fields from a Lua table.
|
||||
void set_config(LuaStack &LS0, LuaSlot tab);
|
||||
|
||||
// Get or Set the request IDs.
|
||||
// This class does just stores these.
|
||||
int64_t request_id() const { return request_id_; }
|
||||
int64_t place_id() const { return place_id_; }
|
||||
int64_t thread_id() const { return thread_id_; }
|
||||
void set_request_id(int64_t request_id) { request_id_ = request_id; }
|
||||
void set_place_id(int64_t place_id) { place_id_ = place_id; }
|
||||
void set_thread_id(int64_t thread_id) { thread_id_ = thread_id; }
|
||||
|
||||
// Get the network target, eg, "cert:host:port"
|
||||
eng::string target() const;
|
||||
|
||||
// Verify that the request is error free and that
|
||||
// defaults have been set.
|
||||
eng::string check() const;
|
||||
|
||||
// Populate an HTTP request from a Lua table.
|
||||
void set_config(LuaStack &LS0, LuaSlot tab);
|
||||
|
||||
// Put the request into the stream, assuming HTTP/1.1
|
||||
void send(StreamBuffer *target) const { send_internal(target, false); }
|
||||
|
||||
@@ -129,14 +129,11 @@ public:
|
||||
eng::string DebugString();
|
||||
};
|
||||
|
||||
class HttpOutRequestMap : public eng::map<int64_t, HttpOutRequest> {
|
||||
public:
|
||||
void serialize(StreamBuffer *sb) const;
|
||||
void deserialize(StreamBuffer *sb);
|
||||
};
|
||||
|
||||
class HttpInResponse {
|
||||
class HttpClientResponse {
|
||||
private:
|
||||
// The request ID.
|
||||
int64_t request_id_;
|
||||
|
||||
// The HTTP response status code.
|
||||
int status_code_;
|
||||
|
||||
@@ -198,7 +195,7 @@ public:
|
||||
const int64_t MAX_CONTENT_LENGTH = 1000000;
|
||||
|
||||
// Construct a blank response.
|
||||
HttpInResponse();
|
||||
HttpClientResponse();
|
||||
|
||||
// Store a result code and an error message, and clear the content.
|
||||
// This is generally used when the client detects an error,
|
||||
@@ -209,7 +206,7 @@ public:
|
||||
// Parse the HTTP response. The closed flag is to be set to true if the
|
||||
// remote has closed the connection.
|
||||
//
|
||||
// If the request is incomplete, generates a 600 incomplete error. In that
|
||||
// If the request is incomplete, generates a status code of zero. In that
|
||||
// case, loading more data from the server might improve the situation.
|
||||
//
|
||||
// Note that the response is not ever removed from the StreamBuffer, which
|
||||
@@ -218,6 +215,14 @@ public:
|
||||
//
|
||||
void parse(const StreamBuffer *sb, bool closed);
|
||||
|
||||
// Return true if the response is complete.
|
||||
bool complete() const { return status_code_ != 0; }
|
||||
|
||||
// Get or Set the request ID.
|
||||
// This class does nothing with the request ID, it just stores it.
|
||||
int64_t request_id() const { return request_id_; }
|
||||
void set_request_id(int64_t v) { request_id_ = v; }
|
||||
|
||||
// Convert the HTTP response to a lua table.
|
||||
void store(LuaStack &LS, LuaSlot tab) const;
|
||||
|
||||
@@ -228,4 +233,25 @@ public:
|
||||
static void store_fail(LuaStack &LS, LuaSlot tab, int status_code, std::string_view error);
|
||||
};
|
||||
|
||||
class HttpClientRequestMap : public eng::map<int64_t, HttpClientRequest> {
|
||||
public:
|
||||
void serialize(StreamBuffer *sb) const;
|
||||
void deserialize(StreamBuffer *sb);
|
||||
};
|
||||
|
||||
using HttpClientResponseVec = eng::vector<HttpClientResponse>;
|
||||
|
||||
// This class associates an HTTP request to an actual communication
|
||||
// channel that is executing that request.
|
||||
|
||||
class HttpClientChannel {
|
||||
public:
|
||||
SharedChannel channel_;
|
||||
int64_t parsed_bytes_;
|
||||
|
||||
HttpClientChannel() : parsed_bytes_(0) {}
|
||||
};
|
||||
|
||||
using HttpClientChannelMap = eng::map<int64_t, HttpClientChannel>;
|
||||
|
||||
#endif // HTTP_HPP
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
LuaConsole console_;
|
||||
ClientVector clients_;
|
||||
PrintChanneler print_channeler_;
|
||||
HttpClientChannelMap http_client_channels_;
|
||||
int64_t admin_id_;
|
||||
Gui gui_;
|
||||
|
||||
@@ -201,6 +202,41 @@ public:
|
||||
while (handle_invocation(client));
|
||||
}
|
||||
util::remove_nullptrs(clients_);
|
||||
|
||||
// Look for new outgoing HTTP client requests.
|
||||
for (const auto &pair : master_->http_requests()) {
|
||||
const HttpClientRequest &request = pair.second;
|
||||
HttpClientChannel &channel = http_client_channels_[request.request_id()];
|
||||
if (channel.channel_ == nullptr) {
|
||||
channel.channel_ = new_outgoing_channel(request.target());
|
||||
channel.parsed_bytes_ = 0;
|
||||
request.send(channel.channel_->out());
|
||||
}
|
||||
}
|
||||
|
||||
// Maintain existing outgoing HTTP client requests.
|
||||
HttpClientResponseVec http_responses;
|
||||
for (auto &pair : http_client_channels_) {
|
||||
HttpClientChannel &htchan = pair.second;
|
||||
Channel &channel = *htchan.channel_;
|
||||
if (channel.closed() || (channel.in()->fill() > htchan.parsed_bytes_)) {
|
||||
HttpClientResponse response;
|
||||
if (!channel.error().empty()) {
|
||||
response.fail(503, util::ss("Service Unavailable: ", channel.error()));
|
||||
} else {
|
||||
htchan.parsed_bytes_ = channel.in()->fill();
|
||||
response.parse(channel.in(), channel.closed());
|
||||
}
|
||||
if (response.complete()) {
|
||||
response.set_request_id(pair.first);
|
||||
http_responses.push_back(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const HttpClientResponse &response : http_responses) {
|
||||
http_client_channels_.erase(response.request_id());
|
||||
}
|
||||
master_->http_responses(http_responses);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -532,14 +532,14 @@ LuaDefine(http_get, "request",
|
||||
LuaArg request;
|
||||
LuaRet response;
|
||||
LuaStack LS(L, request, response);
|
||||
HttpOutRequest req;
|
||||
HttpClientRequest req;
|
||||
|
||||
// Parse the request and make sure it's valid.
|
||||
req.set_config(LS, request);
|
||||
req.set_defaults();
|
||||
eng::string error = req.check();
|
||||
if (!error.empty()) {
|
||||
HttpInResponse::store_fail(LS, response, 400, util::ss("bad request: ", error));
|
||||
HttpClientResponse::store_fail(LS, response, 400, util::ss("bad request: ", error));
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
|
||||
@@ -393,13 +393,13 @@ void World::update_source(const util::LuaSourceVec &source) {
|
||||
assert(stack_is_clear());
|
||||
}
|
||||
|
||||
void World::http_response(int64_t request_id, const HttpInResponse &response) {
|
||||
void World::http_response(const HttpClientResponse &response) {
|
||||
// Find the request.
|
||||
auto iter = http_requests_.find(request_id);
|
||||
auto iter = http_requests_.find(response.request_id());
|
||||
if (iter == http_requests_.end()) {
|
||||
return;
|
||||
}
|
||||
HttpOutRequest request = iter->second;
|
||||
HttpClientRequest request = iter->second;
|
||||
http_requests_.erase(iter);
|
||||
|
||||
// Get the place and thread as lua objects.
|
||||
@@ -443,13 +443,18 @@ void World::http_response(int64_t request_id, const HttpInResponse &response) {
|
||||
run_scheduled_threads();
|
||||
}
|
||||
|
||||
void World::http_responses(const HttpClientResponseVec &responses) {
|
||||
for (const HttpClientResponse &response : responses) {
|
||||
http_response(response);
|
||||
}
|
||||
}
|
||||
|
||||
void World::abort_all_http_requests(int status_code, std::string_view error) {
|
||||
HttpInResponse abortresponse;
|
||||
HttpClientResponse abortresponse;
|
||||
abortresponse.fail(status_code, error);
|
||||
while (true) {
|
||||
auto iter = http_requests_.begin();
|
||||
if (iter == http_requests_.end()) break;
|
||||
http_response(iter->second.request_id(), abortresponse);
|
||||
while (!http_requests_.empty()) {
|
||||
abortresponse.set_request_id(http_requests_.begin()->first);
|
||||
http_response(abortresponse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,8 @@ public:
|
||||
void update_source(const util::LuaSourceVec &source);
|
||||
|
||||
// Supply an HTTP response to an outstanding HTTP request.
|
||||
void http_response(int64_t request_id, const HttpInResponse &response);
|
||||
void http_response(const HttpClientResponse &response);
|
||||
void http_responses(const HttpClientResponseVec &responses);
|
||||
|
||||
// Abort all HTTP requests. This is typically used after
|
||||
// reloading a world from a save-game. The http requests that
|
||||
@@ -230,7 +231,7 @@ public:
|
||||
|
||||
// Get a table showing all outstanding HTTP requests.
|
||||
//
|
||||
const HttpOutRequestMap &http_requests() const { return http_requests_; }
|
||||
const HttpClientRequestMap &http_requests() const { return http_requests_; }
|
||||
|
||||
// Serialize and deserialize.
|
||||
//
|
||||
@@ -515,7 +516,7 @@ private:
|
||||
// Outstanding HTTP requests, indexed by request ID.
|
||||
// Authoritative models only.
|
||||
//
|
||||
HttpOutRequestMap http_requests_;
|
||||
HttpClientRequestMap http_requests_;
|
||||
|
||||
// Serialized snapshot of world model.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user