| #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_ |