Skip to content

Commit a037770

Browse files
jasnellMylesBorins
authored andcommittedMar 9, 2020
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: #32069 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 50094de commit a037770

7 files changed

+862
-474
lines changed
 

‎node.gyp

+2
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@
664664
'src/node_errors.h',
665665
'src/node_file.h',
666666
'src/node_file-inl.h',
667+
'src/node_http_common.h',
668+
'src/node_http_common-inl.h',
667669
'src/node_http2.h',
668670
'src/node_http2_state.h',
669671
'src/node_i18n.h',

‎src/env.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ class IsolateData : public MemoryRetainer {
510510
#undef VS
511511
#undef VP
512512

513-
std::unordered_map<nghttp2_rcbuf*, v8::Eternal<v8::String>> http2_static_strs;
513+
std::unordered_map<const char*, v8::Eternal<v8::String>> http_static_strs;
514514
inline v8::Isolate* isolate() const;
515515
IsolateData(const IsolateData&) = delete;
516516
IsolateData& operator=(const IsolateData&) = delete;

‎src/node_http2.cc

+83-152
Large diffs are not rendered by default.

‎src/node_http2.h

+58-320
Large diffs are not rendered by default.

‎src/node_http_common-inl.h

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

‎src/node_http_common.h

+527
Large diffs are not rendered by default.

‎test/parallel/test-http2-binding.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,16 @@ const expectedHeaderNames = {
170170
HTTP2_HEADER_CONTENT_MD5: 'content-md5',
171171
HTTP2_HEADER_TE: 'te',
172172
HTTP2_HEADER_UPGRADE: 'upgrade',
173-
HTTP2_HEADER_HTTP2_SETTINGS: 'http2-settings'
173+
HTTP2_HEADER_HTTP2_SETTINGS: 'http2-settings',
174+
HTTP2_HEADER_X_XSS_PROTECTION: 'x-xss-protection',
175+
HTTP2_HEADER_ALT_SVC: 'alt-svc',
176+
HTTP2_HEADER_CONTENT_SECURITY_POLICY: 'content-security-policy',
177+
HTTP2_HEADER_EARLY_DATA: 'early-data',
178+
HTTP2_HEADER_EXPECT_CT: 'expect-ct',
179+
HTTP2_HEADER_ORIGIN: 'origin',
180+
HTTP2_HEADER_PURPOSE: 'purpose',
181+
HTTP2_HEADER_TIMING_ALLOW_ORIGIN: 'timing-allow-origin',
182+
HTTP2_HEADER_X_FORWARDED_FOR: 'x-forwarded-for',
174183
};
175184

176185
const expectedNGConstants = {

0 commit comments

Comments
 (0)
Please sign in to comment.