Plack-Handler-H2
view release on metacpan or search on metacpan
lib/Plack/Handler/plack_handler_h2.cc view on Meta::CPAN
if (!ecdh) {
errx(1, "EC_KEY_new_by_curv_name failed: %s",
ERR_error_string(ERR_get_error(), NULL));
}
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
EC_KEY_free(ecdh);
}
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file.c_str(),
SSL_FILETYPE_PEM) != 1) {
croak("Could not read private key file");
}
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file.c_str()) != 1) {
croak("Could not read certificate file");
}
SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
return ssl_ctx;
}
static inline bool ends_with(const std::string &str,
const std::string &suffix) {
if (str.length() < suffix.length()) {
return false;
}
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) ==
0;
}
static inline std::string double_dot = "/..";
static inline std::string single_dot = "/.";
static inline int check_path(std::string &path) {
/* We don't like '\' in url. */
return path[0] && path[0] == '/' && strchr(path.c_str(), '\\') == NULL &&
strstr(path.c_str(), double_dot.c_str()) == NULL &&
strstr(path.c_str(), single_dot.c_str()) == NULL &&
!ends_with(path, double_dot) && !ends_with(path, single_dot);
}
static inline H2Session *get_session_from_sv_uv(SV *session_sv) {
if (!SvOK(session_sv) || !SvIOK(session_sv)) {
warn("Session SV is not OK (undefined or invalid)");
return nullptr;
}
uintptr_t session_ptr = SvUV(session_sv);
if (session_ptr == 0) {
warn("Session pointer is null");
return nullptr;
}
return reinterpret_cast<H2Session *>(session_ptr);
}
static inline int32_t send_headers_from_av(H2Session *session,
int32_t stream_id, SV *status_sv,
SV *headers_sv,
bool streaming = false) {
int status = SvIV(status_sv);
AV *headers_av =
(headers_sv && SvROK(headers_sv)) ? (AV *)SvRV(headers_sv) : nullptr;
auto data_it = std::find_if(
session->data.begin(), session->data.end(),
[stream_id](H2Data *d) { return d->stream_id == stream_id; });
if (data_it == session->data.end()) {
croak("Stream data not found for stream_id %d", stream_id);
}
H2Data *data = *data_it;
auto &header_storage = *data->response_headers;
header_storage.clear();
std::vector<nghttp2_nv> nva;
header_storage.push_back(":status");
header_storage.push_back(std::to_string(status));
nghttp2_nv status_header = {
(uint8_t *)header_storage[header_storage.size() - 2].c_str(),
(uint8_t *)header_storage[header_storage.size() - 1].c_str(), 7,
header_storage[header_storage.size() - 1].length(), NGHTTP2_NV_FLAG_NONE};
nva.push_back(status_header);
// Add other headers
if (headers_av) {
int header_count = av_len(headers_av) + 1;
for (int i = 0; i < header_count; i += 2) {
SV **name_sv = av_fetch(headers_av, i, 0);
SV **value_sv = av_fetch(headers_av, i + 1, 0);
if (name_sv && value_sv) {
size_t name_len, value_len;
const char *name = SvPV(*name_sv, name_len);
const char *value = SvPV(*value_sv, value_len);
std::string lowercase_name(name, name_len);
std::transform(lowercase_name.begin(), lowercase_name.end(),
lowercase_name.begin(), ::tolower);
// Skip content-length for streaming responses (when with_end_stream is
// false) HTTP/2 doesn't need content-length for streaming
if (streaming && lowercase_name == "content-length") {
continue;
}
header_storage.push_back(lowercase_name);
header_storage.push_back(std::string(value, value_len));
nghttp2_nv header = {
(uint8_t *)header_storage[header_storage.size() - 2].c_str(),
(uint8_t *)header_storage[header_storage.size() - 1].c_str(),
lowercase_name.length(), value_len, NGHTTP2_NV_FLAG_NONE};
nva.push_back(header);
}
}
}
nghttp2_submit_headers(session->session.get(), NGHTTP2_FLAG_END_HEADERS,
stream_id, NULL, nva.data(), nva.size(), NULL);
return session->send();
}
static const char RESPONSE_500[] =
"<html><head><title>500</title></head><body><h1>500 Internal Server "
"Error</h1></body></html>";
static inline int32_t error_reply(H2Session *session, H2Data *data) {
int pipefd[2];
int rv = pipe(pipefd);
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
if (rv != 0) {
warn("pipe() failed, errno=%d", errno);
rv = nghttp2_submit_rst_stream(session->session.get(), NGHTTP2_FLAG_NONE,
data->stream_id, NGHTTP2_INTERNAL_ERROR);
if (rv != 0) {
warn("nghttp2_submit_rst_stream() failed, rv=%d", rv);
return -1;
}
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
ssize_t writelen = write(pipefd[1], RESPONSE_500, sizeof(RESPONSE_500) - 1);
close(pipefd[1]);
if (writelen != sizeof(RESPONSE_500) - 1) {
close(pipefd[0]);
warn("write() failed, errno=%d", errno);
return -1;
}
data->fd = pipefd[0];
rv = nghttp2_submit_headers(session->session.get(), 0, data->stream_id, NULL,
hdrs, sizeof(hdrs) / sizeof(hdrs[0]), NULL);
if (rv != 0) {
warn("nghttp2_submit_headers() failed: %s", nghttp2_strerror(rv));
close(data->fd);
return -1;
}
// Create the data provider
lib/Plack/Handler/plack_handler_h2.cc view on Meta::CPAN
newSVpv(session->client_address.c_str(),
session->client_address.length()));
hv_stores(env, "QUERY_STRING",
newSVpv(data->query ? data->query->c_str() : "",
data->query ? data->query->length() : 0));
hv_stores(env, "HTTPS", newSVpvs("on"));
const auto &headers = *data->headers;
std::string header_prefix = "HTTP_";
for (const auto &[key, value] : headers) {
std::string header_name = "";
for (char c : key) {
if (c == '-') {
header_name += '_';
} else {
header_name += std::toupper(c);
}
}
// Plack's convention is to store CONTENT_LENGTH and CONTENT_TYPE without
// the HTTP_ prefix
if (header_name != "CONTENT_LENGTH" && header_name != "CONTENT_TYPE") {
header_name = header_prefix + header_name;
}
SV *header_value = newSVpv(value.c_str(), value.length());
hv_store(env, header_name.c_str(), header_name.length(), header_value, 0);
}
// Append HTTP/2 pseudo-headers
{
SV *header_value;
std::string h2_header_name = "HTTP_2_";
std::string authority = data->authority ? *data->authority : "";
header_value = newSVpv(authority.c_str(), authority.length());
hv_store(env, (h2_header_name + "AUTHORITY").c_str(),
(h2_header_name + "AUTHORITY").length(), header_value, 0);
std::string scheme = data->scheme ? *data->scheme : "https";
header_value = newSVpv(scheme.c_str(), scheme.length());
hv_store(env, (h2_header_name + "SCHEME").c_str(),
(h2_header_name + "SCHEME").length(), header_value, 0);
std::string path = data->path ? *data->path : "/";
header_value = newSVpv(path.c_str(), path.length());
hv_store(env, (h2_header_name + "PATH").c_str(),
(h2_header_name + "PATH").length(), header_value, 0);
std::string method = data->method ? *data->method : "GET";
header_value = newSVpv(method.c_str(), method.length());
hv_store(env, (h2_header_name + "METHOD").c_str(),
(h2_header_name + "METHOD").length(), header_value, 0);
}
AV *version = newAV();
av_store(version, 0, newSViv(1));
av_store(version, 1, newSViv(1));
hv_stores(env, "psgi.version", newRV_noinc((SV *)version));
hv_stores(env, "psgi.url_scheme", newSVpvs("https"));
hv_stores(env, "psgi.errors", newRV_inc((SV *)PL_stderrgv));
hv_stores(env, "psgi.multithread", &PL_sv_no);
hv_stores(env, "psgi.multiprocess", &PL_sv_yes);
hv_stores(env, "psgi.run_once", &PL_sv_no);
hv_stores(env, "psgi.streaming", &PL_sv_yes);
hv_stores(env, "psgi.nonblocking", &PL_sv_no);
hv_stores(env, "psgix.h2.stream_id", newSViv(data->stream_id));
SV *input_sv = nullptr;
if (data->body_fd != -1) {
lseek(data->body_fd, 0, SEEK_SET); // Rewind to start
PerlIO *pio = PerlIO_fdopen(data->body_fd, "r");
if (pio) {
GV *gv = newGVgen("Plack::Handler::H2");
IO *io = GvIOn(gv);
IoIFP(io) = pio;
IoOFP(io) = pio;
IoTYPE(io) = IoTYPE_RDONLY;
input_sv = newRV_noinc((SV *)gv);
data->body_fd = -1;
} else {
warn("Failed to create PerlIO from fd: %s", strerror(errno));
input_sv = newSVpvs("");
}
} else if (data->body_buffer && !data->body_buffer->empty()) {
int pipefd[2];
if (pipe(pipefd) == 0) {
ssize_t written = write(pipefd[1], data->body_buffer->c_str(),
data->body_buffer->size());
close(pipefd[1]);
if (written == (ssize_t)data->body_buffer->size()) {
PerlIO *pio = PerlIO_fdopen(pipefd[0], "r");
if (pio) {
GV *gv = newGVgen("Plack::Handler::H2");
IO *io = GvIOn(gv);
IoIFP(io) = pio;
IoOFP(io) = pio;
IoTYPE(io) = IoTYPE_RDONLY;
input_sv = newRV_noinc((SV *)gv);
} else {
close(pipefd[0]);
input_sv = newSVpvs("");
}
} else {
close(pipefd[0]);
input_sv = newSVpvs("");
}
} else {
input_sv = newSVpvs("");
}
} else {
int pipefd[2];
if (pipe(pipefd) == 0) {
close(pipefd[1]);
PerlIO *pio = PerlIO_fdopen(pipefd[0], "r");
if (pio) {
GV *gv = newGVgen("Plack::Handler::H2");
IO *io = GvIOn(gv);
IoIFP(io) = pio;
IoOFP(io) = pio;
IoTYPE(io) = IoTYPE_RDONLY;
input_sv = newRV_noinc((SV *)gv);
} else {
close(pipefd[0]);
input_sv = newSVpvs("");
( run in 0.755 second using v1.01-cache-2.11-cpan-0b5f733616e )