diff options
author | James M Snell <jasnell@gmail.com> | 2020-03-03 04:27:06 +0300 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2020-03-06 18:19:12 +0300 |
commit | 434d39dd670845cafd122fadb0a75d72c1658b05 (patch) | |
tree | 878d76ff4b659bcb227de9f72a12421aaf133093 /src/node_http_common-inl.h | |
parent | b6cd2155c3442a8c56aa6f6ffa0dc9b6a308a7b1 (diff) |
src,http2: introduce node_http_common
The nghttp2 and nghttp3 (used in the QUIC implementation) share nearly
identical structs for header handling. However, they differ enough that
they need to be handled slightly different in each case. This PR
includes some elements introduced in the QUIC PR separated out to
make them independently reviewable, and updates the http2 implementation
to use the shared utilities.
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/32069
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'src/node_http_common-inl.h')
-rw-r--r-- | src/node_http_common-inl.h | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/node_http_common-inl.h b/src/node_http_common-inl.h new file mode 100644 index 00000000000..1bc7b46d63a --- /dev/null +++ b/src/node_http_common-inl.h @@ -0,0 +1,181 @@ +#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 + : token_(other.token_), + name_(std::move(other.name_)), + value_(std::move(other.value_)), + 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()->http_static_strs; + 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_ |