nghttpx: Refactor API downstream connection to allow more endpoints
This commit is contained in:
parent
0797e89a90
commit
dc15832030
|
@ -33,8 +33,27 @@
|
|||
|
||||
namespace shrpx {
|
||||
|
||||
namespace {
|
||||
// List of API endpoints
|
||||
const APIEndpoint apis[] = {
|
||||
APIEndpoint{
|
||||
StringRef::from_lit("/api/v1beta1/backendconfig"), true,
|
||||
(1 << API_METHOD_POST) | (1 << API_METHOD_PUT),
|
||||
&APIDownstreamConnection::handle_backendconfig,
|
||||
},
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// The method string. This must be same order of APIMethod.
|
||||
constexpr StringRef API_METHOD_STRING[] = {
|
||||
StringRef::from_lit("GET"), StringRef::from_lit("POST"),
|
||||
StringRef::from_lit("PUT"),
|
||||
};
|
||||
} // namespace
|
||||
|
||||
APIDownstreamConnection::APIDownstreamConnection(Worker *worker)
|
||||
: worker_(worker), abandoned_(false) {}
|
||||
: worker_(worker), api_(nullptr), shutdown_read_(false) {}
|
||||
|
||||
APIDownstreamConnection::~APIDownstreamConnection() {}
|
||||
|
||||
|
@ -64,7 +83,7 @@ enum {
|
|||
|
||||
int APIDownstreamConnection::send_reply(unsigned int http_status,
|
||||
int api_status) {
|
||||
abandoned_ = true;
|
||||
shutdown_read_ = true;
|
||||
|
||||
auto upstream = downstream_->get_upstream();
|
||||
|
||||
|
@ -130,23 +149,45 @@ int APIDownstreamConnection::send_reply(unsigned int http_status,
|
|||
|
||||
int APIDownstreamConnection::push_request_headers() {
|
||||
auto &req = downstream_->request();
|
||||
auto &resp = downstream_->response();
|
||||
|
||||
auto path =
|
||||
StringRef{std::begin(req.path),
|
||||
std::find(std::begin(req.path), std::end(req.path), '?')};
|
||||
|
||||
if (path != StringRef::from_lit("/api/v1beta1/backendconfig")) {
|
||||
for (auto &p : apis) {
|
||||
if (p.path == path) {
|
||||
api_ = &p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!api_) {
|
||||
send_reply(404, API_FAILURE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (req.method != HTTP_POST && req.method != HTTP_PUT) {
|
||||
resp.fs.add_header_token(StringRef::from_lit("allow"),
|
||||
StringRef::from_lit("POST, PUT"), false, -1);
|
||||
send_reply(405, API_FAILURE);
|
||||
|
||||
switch (req.method) {
|
||||
case HTTP_GET:
|
||||
if (!(api_->allowed_methods & (1 << API_METHOD_GET))) {
|
||||
error_method_not_allowed();
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case HTTP_POST:
|
||||
if (!(api_->allowed_methods & (1 << API_METHOD_POST))) {
|
||||
error_method_not_allowed();
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case HTTP_PUT:
|
||||
if (!(api_->allowed_methods & (1 << API_METHOD_PUT))) {
|
||||
error_method_not_allowed();
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error_method_not_allowed();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -161,9 +202,42 @@ int APIDownstreamConnection::push_request_headers() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APIDownstreamConnection::error_method_not_allowed() {
|
||||
auto &resp = downstream_->response();
|
||||
|
||||
size_t len = 0;
|
||||
for (uint8_t i = 0; i < API_METHOD_MAX; ++i) {
|
||||
if (api_->allowed_methods & (1 << i)) {
|
||||
// The length of method + ", "
|
||||
len += API_METHOD_STRING[i].size() + 2;
|
||||
}
|
||||
}
|
||||
|
||||
assert(len > 0);
|
||||
|
||||
auto &balloc = downstream_->get_block_allocator();
|
||||
|
||||
auto iov = make_byte_ref(balloc, len + 1);
|
||||
auto p = iov.base;
|
||||
for (uint8_t i = 0; i < API_METHOD_MAX; ++i) {
|
||||
if (api_->allowed_methods & (1 << i)) {
|
||||
auto &s = API_METHOD_STRING[i];
|
||||
p = std::copy(std::begin(s), std::end(s), p);
|
||||
p = std::copy_n(", ", 2, p);
|
||||
}
|
||||
}
|
||||
|
||||
p -= 2;
|
||||
*p = '\0';
|
||||
|
||||
resp.fs.add_header_token(StringRef::from_lit("allow"), StringRef{iov.base, p},
|
||||
false, -1);
|
||||
return send_reply(405, API_FAILURE);
|
||||
}
|
||||
|
||||
int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
||||
size_t datalen) {
|
||||
if (abandoned_) {
|
||||
if (shutdown_read_ || !api_->require_body) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -187,10 +261,14 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
|||
}
|
||||
|
||||
int APIDownstreamConnection::end_upload_data() {
|
||||
if (abandoned_) {
|
||||
if (shutdown_read_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return api_->handler(*this);
|
||||
}
|
||||
|
||||
int APIDownstreamConnection::handle_backendconfig() {
|
||||
auto output = downstream_->get_request_buf();
|
||||
|
||||
std::array<struct iovec, 2> iov;
|
||||
|
|
|
@ -26,11 +26,36 @@
|
|||
#define SHRPX_API_DOWNSTREAM_CONNECTION_H
|
||||
|
||||
#include "shrpx_downstream_connection.h"
|
||||
#include "template.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
class Worker;
|
||||
|
||||
// If new method is added, don't forget to update API_METHOD_STRING as
|
||||
// well.
|
||||
enum APIMethod {
|
||||
API_METHOD_GET,
|
||||
API_METHOD_POST,
|
||||
API_METHOD_PUT,
|
||||
API_METHOD_MAX,
|
||||
};
|
||||
|
||||
class APIDownstreamConnection;
|
||||
|
||||
struct APIEndpoint {
|
||||
// Endpoint path. It must start with "/api/".
|
||||
StringRef path;
|
||||
// true if we evaluate request body.
|
||||
bool require_body;
|
||||
// Allowed methods. This is bitwise OR of one or more of (1 <<
|
||||
// APIMethod value).
|
||||
uint8_t allowed_methods;
|
||||
std::function<int(APIDownstreamConnection &)> handler;
|
||||
};
|
||||
|
||||
class APIDownstreamConnection : public DownstreamConnection {
|
||||
public:
|
||||
APIDownstreamConnection(Worker *worker);
|
||||
|
@ -59,10 +84,17 @@ public:
|
|||
virtual DownstreamAddr *get_addr() const;
|
||||
|
||||
int send_reply(unsigned int http_status, int api_status);
|
||||
int error_method_not_allowed();
|
||||
|
||||
// Handles backendconfig API request.
|
||||
int handle_backendconfig();
|
||||
|
||||
private:
|
||||
Worker *worker_;
|
||||
bool abandoned_;
|
||||
// This points to the requested APIEndpoint struct.
|
||||
const APIEndpoint *api_;
|
||||
// true if we stop reading request body.
|
||||
bool shutdown_read_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
Loading…
Reference in New Issue