blob: 2c76dc3a1fd372c8e996b61475a0a148a2e11b7f [file] [log] [blame]
#ifndef SRC_NODE_HTTP_COMMON_INL_H_
#define SRC_NODE_HTTP_COMMON_INL_H_
#include "node_http_common.h"
#include "node.h"
#include "node_mem-inl.h"
#include "env-inl.h"
#include "v8.h"
#include <algorithm>
namespace node {
template <typename T>
NgHeaders<T>::NgHeaders(Environment* env, v8::Local<v8::Array> headers) {
v8::Local<v8::Value> header_string =
headers->Get(env->context(), 0).ToLocalChecked();
v8::Local<v8::Value> header_count =
headers->Get(env->context(), 1).ToLocalChecked();
CHECK(header_count->IsUint32());
CHECK(header_string->IsString());
count_ = header_count.As<v8::Uint32>()->Value();
int header_string_len = header_string.As<v8::String>()->Length();
if (count_ == 0) {
CHECK_EQ(header_string_len, 0);
return;
}
buf_.AllocateSufficientStorage((alignof(nv_t) - 1) +
count_ * sizeof(nv_t) +
header_string_len);
char* start = reinterpret_cast<char*>(
RoundUp(reinterpret_cast<uintptr_t>(*buf_), alignof(nv_t)));
char* header_contents = start + (count_ * sizeof(nv_t));
nv_t* const nva = reinterpret_cast<nv_t*>(start);
CHECK_LE(header_contents + header_string_len, *buf_ + buf_.length());
CHECK_EQ(header_string.As<v8::String>()->WriteOneByte(
env->isolate(),
reinterpret_cast<uint8_t*>(header_contents),
0,
header_string_len,
v8::String::NO_NULL_TERMINATION),
header_string_len);
size_t n = 0;
char* p;
for (p = header_contents; p < header_contents + header_string_len; n++) {
if (n >= count_) {
static uint8_t zero = '\0';
nva[0].name = nva[0].value = &zero;
nva[0].namelen = nva[0].valuelen = 1;
count_ = 1;
return;
}
nva[n].flags = T::kNoneFlag;
nva[n].name = reinterpret_cast<uint8_t*>(p);
nva[n].namelen = strlen(p);
p += nva[n].namelen + 1;
nva[n].value = reinterpret_cast<uint8_t*>(p);
nva[n].valuelen = strlen(p);
p += nva[n].valuelen + 1;
}
}
size_t GetClientMaxHeaderPairs(size_t max_header_pairs) {
static constexpr size_t min_header_pairs = 1;
return std::max(max_header_pairs, min_header_pairs);
}
size_t GetServerMaxHeaderPairs(size_t max_header_pairs) {
static constexpr size_t min_header_pairs = 4;
return std::max(max_header_pairs, min_header_pairs);
}
template <typename T>
bool NgHeader<T>::IsZeroLength(
NgHeader<T>::rcbuf_t* name,
NgHeader<T>::rcbuf_t* value) {
return IsZeroLength(-1, name, value);
}
template <typename T>
bool NgHeader<T>::IsZeroLength(
int32_t token,
NgHeader<T>::rcbuf_t* name,
NgHeader<T>::rcbuf_t* value) {
if (NgHeader<T>::rcbufferpointer_t::IsZeroLength(value))
return true;
const char* header_name = T::ToHttpHeaderName(token);
return header_name != nullptr ||
NgHeader<T>::rcbufferpointer_t::IsZeroLength(name);
}
template <typename T>
NgHeader<T>::NgHeader(
Environment* env,
NgHeader<T>::rcbuf_t* name,
NgHeader<T>::rcbuf_t* value,
uint8_t flags)
: NgHeader<T>(env, -1, name, value, flags) {}
template <typename T>
NgHeader<T>::NgHeader(
Environment* env,
int32_t token,
NgHeader<T>::rcbuf_t* name,
NgHeader<T>::rcbuf_t* value,
uint8_t flags) : env_(env), token_(token), flags_(flags) {
if (token == -1) {
CHECK_NOT_NULL(name);
name_.reset(name, true); // Internalizable
}
CHECK_NOT_NULL(value);
name_.reset(name, true); // Internalizable
value_.reset(value);
}
template <typename T>
NgHeader<T>::NgHeader(NgHeader<T>&& other) noexcept
: name_(std::move(other.name_)),
value_(std::move(other.value_)),
token_(other.token_),
flags_(other.flags_) {
other.token_ = -1;
other.flags_ = 0;
other.env_ = nullptr;
}
template <typename T>
v8::MaybeLocal<v8::String> NgHeader<T>::GetName(
NgHeader<T>::allocator_t* allocator) const {
// Not all instances will support using token id's for header names.
// HTTP/2 specifically does not support it.
const char* header_name = T::ToHttpHeaderName(token_);
// If header_name is not nullptr, then it is a known header with
// a statically defined name. We can safely internalize it here.
if (header_name != nullptr) {
auto& static_str_map = env_->isolate_data()->static_str_map;
v8::Eternal<v8::String> eternal = static_str_map[header_name];
if (eternal.IsEmpty()) {
v8::Local<v8::String> str = OneByteString(env_->isolate(), header_name);
eternal.Set(env_->isolate(), str);
return str;
}
return eternal.Get(env_->isolate());
}
return rcbufferpointer_t::External::New(allocator, name_);
}
template <typename T>
v8::MaybeLocal<v8::String> NgHeader<T>::GetValue(
NgHeader<T>::allocator_t* allocator) const {
return rcbufferpointer_t::External::New(allocator, value_);
}
template <typename T>
std::string NgHeader<T>::name() const {
return name_.str();
}
template <typename T>
std::string NgHeader<T>::value() const {
return value_.str();
}
template <typename T>
size_t NgHeader<T>::length() const {
return name_.len() + value_.len();
}
} // namespace node
#endif // SRC_NODE_HTTP_COMMON_INL_H_