1 |
|
|
#ifndef SRC_NODE_HTTP_COMMON_INL_H_ |
2 |
|
|
#define SRC_NODE_HTTP_COMMON_INL_H_ |
3 |
|
|
|
4 |
|
|
#include "node_http_common.h" |
5 |
|
|
#include "node.h" |
6 |
|
|
#include "node_mem-inl.h" |
7 |
|
|
#include "env-inl.h" |
8 |
|
|
#include "v8.h" |
9 |
|
|
|
10 |
|
|
#include <algorithm> |
11 |
|
|
|
12 |
|
|
namespace node { |
13 |
|
|
|
14 |
|
|
template <typename T> |
15 |
|
23956 |
NgHeaders<T>::NgHeaders(Environment* env, v8::Local<v8::Array> headers) { |
16 |
|
|
v8::Local<v8::Value> header_string = |
17 |
|
47912 |
headers->Get(env->context(), 0).ToLocalChecked(); |
18 |
|
|
v8::Local<v8::Value> header_count = |
19 |
|
47912 |
headers->Get(env->context(), 1).ToLocalChecked(); |
20 |
✗✓ |
23956 |
CHECK(header_count->IsUint32()); |
21 |
✗✓ |
47912 |
CHECK(header_string->IsString()); |
22 |
|
23956 |
count_ = header_count.As<v8::Uint32>()->Value(); |
23 |
|
23956 |
int header_string_len = header_string.As<v8::String>()->Length(); |
24 |
|
|
|
25 |
✓✓ |
23956 |
if (count_ == 0) { |
26 |
✗✓ |
31 |
CHECK_EQ(header_string_len, 0); |
27 |
|
32 |
return; |
28 |
|
|
} |
29 |
|
|
|
30 |
|
47850 |
buf_.AllocateSufficientStorage((alignof(nv_t) - 1) + |
31 |
|
23925 |
count_ * sizeof(nv_t) + |
32 |
|
|
header_string_len); |
33 |
|
|
|
34 |
|
23925 |
char* start = AlignUp(buf_.out(), alignof(nv_t)); |
35 |
|
23925 |
char* header_contents = start + (count_ * sizeof(nv_t)); |
36 |
|
23925 |
nv_t* const nva = reinterpret_cast<nv_t*>(start); |
37 |
|
|
|
38 |
✗✓ |
23925 |
CHECK_LE(header_contents + header_string_len, *buf_ + buf_.length()); |
39 |
✗✓ |
23925 |
CHECK_EQ(header_string.As<v8::String>()->WriteOneByte( |
40 |
|
|
env->isolate(), |
41 |
|
|
reinterpret_cast<uint8_t*>(header_contents), |
42 |
|
|
0, |
43 |
|
|
header_string_len, |
44 |
|
|
v8::String::NO_NULL_TERMINATION), |
45 |
|
|
header_string_len); |
46 |
|
|
|
47 |
|
23925 |
size_t n = 0; |
48 |
|
|
char* p; |
49 |
✓✓ |
97372 |
for (p = header_contents; p < header_contents + header_string_len; n++) { |
50 |
✓✓ |
73448 |
if (n >= count_) { |
51 |
|
|
static uint8_t zero = '\0'; |
52 |
|
1 |
nva[0].name = nva[0].value = &zero; |
53 |
|
1 |
nva[0].namelen = nva[0].valuelen = 1; |
54 |
|
1 |
count_ = 1; |
55 |
|
1 |
return; |
56 |
|
|
} |
57 |
|
|
|
58 |
|
73447 |
nva[n].name = reinterpret_cast<uint8_t*>(p); |
59 |
|
73447 |
nva[n].namelen = strlen(p); |
60 |
|
73447 |
p += nva[n].namelen + 1; |
61 |
|
73447 |
nva[n].value = reinterpret_cast<uint8_t*>(p); |
62 |
|
73447 |
nva[n].valuelen = strlen(p); |
63 |
|
73447 |
p += nva[n].valuelen + 1; |
64 |
|
73447 |
nva[n].flags = *p; |
65 |
|
73447 |
p++; |
66 |
|
|
} |
67 |
|
|
} |
68 |
|
|
|
69 |
|
377 |
size_t GetClientMaxHeaderPairs(size_t max_header_pairs) { |
70 |
|
|
static constexpr size_t min_header_pairs = 1; |
71 |
|
377 |
return std::max(max_header_pairs, min_header_pairs); |
72 |
|
|
} |
73 |
|
|
|
74 |
|
393 |
size_t GetServerMaxHeaderPairs(size_t max_header_pairs) { |
75 |
|
|
static constexpr size_t min_header_pairs = 4; |
76 |
|
393 |
return std::max(max_header_pairs, min_header_pairs); |
77 |
|
|
} |
78 |
|
|
|
79 |
|
|
template <typename allocator_t> |
80 |
|
|
std::string NgHeaderBase<allocator_t>::ToString() const { |
81 |
|
|
std::string ret = name(); |
82 |
|
|
ret += " = "; |
83 |
|
|
ret += value(); |
84 |
|
|
return ret; |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
template <typename T> |
88 |
|
|
bool NgHeader<T>::IsZeroLength( |
89 |
|
|
NgHeader<T>::rcbuf_t* name, |
90 |
|
|
NgHeader<T>::rcbuf_t* value) { |
91 |
|
|
return IsZeroLength(-1, name, value); |
92 |
|
|
} |
93 |
|
|
|
94 |
|
|
template <typename T> |
95 |
|
|
bool NgHeader<T>::IsZeroLength( |
96 |
|
|
int32_t token, |
97 |
|
|
NgHeader<T>::rcbuf_t* name, |
98 |
|
|
NgHeader<T>::rcbuf_t* value) { |
99 |
|
|
|
100 |
|
|
if (NgHeader<T>::rcbufferpointer_t::IsZeroLength(value)) |
101 |
|
|
return true; |
102 |
|
|
|
103 |
|
|
const char* header_name = T::ToHttpHeaderName(token); |
104 |
|
|
return header_name != nullptr || |
105 |
|
|
NgHeader<T>::rcbufferpointer_t::IsZeroLength(name); |
106 |
|
|
} |
107 |
|
|
|
108 |
|
|
template <typename T> |
109 |
|
72680 |
NgHeader<T>::NgHeader( |
110 |
|
|
Environment* env, |
111 |
|
|
NgHeader<T>::rcbuf_t* name, |
112 |
|
|
NgHeader<T>::rcbuf_t* value, |
113 |
|
|
uint8_t flags) |
114 |
|
72680 |
: NgHeader<T>(env, -1, name, value, flags) {} |
115 |
|
|
|
116 |
|
|
template <typename T> |
117 |
|
72680 |
NgHeader<T>::NgHeader( |
118 |
|
|
Environment* env, |
119 |
|
|
int32_t token, |
120 |
|
|
NgHeader<T>::rcbuf_t* name, |
121 |
|
|
NgHeader<T>::rcbuf_t* value, |
122 |
|
72680 |
uint8_t flags) : env_(env), token_(token), flags_(flags) { |
123 |
✓✗ |
72680 |
if (token == -1) { |
124 |
✗✓ |
72680 |
CHECK_NOT_NULL(name); |
125 |
|
72680 |
name_.reset(name, true); // Internalizable |
126 |
|
|
} |
127 |
✗✓ |
72680 |
CHECK_NOT_NULL(value); |
128 |
|
72680 |
name_.reset(name, true); // Internalizable |
129 |
|
72680 |
value_.reset(value); |
130 |
|
72680 |
} |
131 |
|
|
|
132 |
|
|
template <typename T> |
133 |
|
72725 |
NgHeader<T>::NgHeader(NgHeader<T>&& other) noexcept |
134 |
|
72725 |
: env_(other.env_), |
135 |
|
72725 |
name_(std::move(other.name_)), |
136 |
|
72725 |
value_(std::move(other.value_)), |
137 |
|
72725 |
token_(other.token_), |
138 |
|
72725 |
flags_(other.flags_) { |
139 |
|
72725 |
other.token_ = -1; |
140 |
|
72725 |
other.flags_ = 0; |
141 |
|
72725 |
other.env_ = nullptr; |
142 |
|
72725 |
} |
143 |
|
|
|
144 |
|
|
template <typename T> |
145 |
|
|
void NgHeader<T>::MemoryInfo(MemoryTracker* tracker) const { |
146 |
|
|
tracker->TrackField("name", name_); |
147 |
|
|
tracker->TrackField("value", value_); |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
template <typename T> |
151 |
|
72660 |
v8::MaybeLocal<v8::String> NgHeader<T>::GetName( |
152 |
|
|
NgHeader<T>::allocator_t* allocator) const { |
153 |
|
|
|
154 |
|
|
// Not all instances will support using token id's for header names. |
155 |
|
|
// HTTP/2 specifically does not support it. |
156 |
|
72660 |
const char* header_name = T::ToHttpHeaderName(token_); |
157 |
|
|
|
158 |
|
|
// If header_name is not nullptr, then it is a known header with |
159 |
|
|
// a statically defined name. We can safely internalize it here. |
160 |
✗✓ |
72660 |
if (header_name != nullptr) { |
161 |
|
|
auto& static_str_map = env_->isolate_data()->static_str_map; |
162 |
|
|
v8::Eternal<v8::String> eternal = static_str_map[header_name]; |
163 |
|
|
if (eternal.IsEmpty()) { |
164 |
|
|
v8::Local<v8::String> str = OneByteString(env_->isolate(), header_name); |
165 |
|
|
eternal.Set(env_->isolate(), str); |
166 |
|
|
return str; |
167 |
|
|
} |
168 |
|
|
return eternal.Get(env_->isolate()); |
169 |
|
|
} |
170 |
|
72660 |
return rcbufferpointer_t::External::New(allocator, name_); |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
template <typename T> |
174 |
|
72660 |
v8::MaybeLocal<v8::String> NgHeader<T>::GetValue( |
175 |
|
|
NgHeader<T>::allocator_t* allocator) const { |
176 |
|
72660 |
return rcbufferpointer_t::External::New(allocator, value_); |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
template <typename T> |
180 |
|
|
std::string NgHeader<T>::name() const { |
181 |
|
|
return name_.str(); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
template <typename T> |
185 |
|
|
std::string NgHeader<T>::value() const { |
186 |
|
|
return value_.str(); |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
template <typename T> |
190 |
|
72680 |
size_t NgHeader<T>::length() const { |
191 |
|
72680 |
return name_.len() + value_.len(); |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
template <typename T> |
195 |
|
72660 |
uint8_t NgHeader<T>::flags() const { |
196 |
|
72660 |
return flags_; |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
} // namespace node |
200 |
|
|
|
201 |
|
|
#endif // SRC_NODE_HTTP_COMMON_INL_H_ |