Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
nghttp2
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
nghttp2
Commits
8e3406ad
Commit
8e3406ad
authored
Jan 03, 2015
by
Tatsuhiro Tsujikawa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nghttpd: Use faster request header handling
parent
aaf0dc82
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
353 additions
and
38 deletions
+353
-38
genheaderfunc.py
genheaderfunc.py
+88
-0
src/HttpServer.cc
src/HttpServer.cc
+27
-38
src/HttpServer.h
src/HttpServer.h
+1
-0
src/http2.cc
src/http2.cc
+198
-0
src/http2.h
src/http2.h
+39
-0
No files found.
genheaderfunc.py
0 → 100755
View file @
8e3406ad
#!/usr/bin/env python
HEADERS
=
[
':authority'
,
':method'
,
':path'
,
':scheme'
,
# disallowed h1 headers
'connection'
,
'expect'
,
'host'
,
'if-modified-since'
,
'keep-alive'
,
'proxy-connection'
,
'te'
,
'transfer-encoding'
,
'upgrade'
]
def
to_enum_hd
(
k
):
res
=
'HD_'
for
c
in
k
.
upper
():
if
c
==
':'
:
continue
if
c
==
'-'
:
res
+=
'_'
continue
res
+=
c
return
res
def
build_header
(
headers
):
res
=
{}
for
k
in
headers
:
size
=
len
(
k
)
if
size
not
in
res
:
res
[
size
]
=
{}
ent
=
res
[
size
]
c
=
k
[
-
1
]
if
c
not
in
ent
:
ent
[
c
]
=
[]
ent
[
c
].
append
(
k
)
return
res
def
gen_enum
():
print
'''
\
enum {'''
for
k
in
sorted
(
HEADERS
):
print
'''
\
{},'''
.
format
(
to_enum_hd
(
k
))
print
'''
\
HD_MAXIDX,
};'''
def
gen_index_header
():
print
'''
\
void index_header(int *hdidx, const uint8_t *s, size_t len, size_t idx) {
switch (len) {'''
b
=
build_header
(
HEADERS
)
for
size
in
sorted
(
b
.
keys
()):
ents
=
b
[
size
]
print
'''
\
case {}:'''
.
format
(
size
)
print
'''
\
switch (util::lowcase(s[len - 1])) {'''
for
c
in
sorted
(
ents
.
keys
()):
headers
=
sorted
(
ents
[
c
])
print
'''
\
case '{}':'''
.
format
(
c
)
for
k
in
headers
:
print
'''
\
if (util::strieq("{}", s, {})) {{
hdidx[{}] = idx;
return;
}}'''
.
format
(
k
[:
-
1
],
size
-
1
,
to_enum_hd
(
k
))
print
'''
\
break;'''
print
'''
\
}
break;'''
print
'''
\
}
}'''
if
__name__
==
'__main__'
:
gen_enum
()
print
''
gen_index_header
()
src/HttpServer.cc
View file @
8e3406ad
...
...
@@ -89,7 +89,9 @@ void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
namespace
{
void
append_nv
(
Stream
*
stream
,
const
std
::
vector
<
nghttp2_nv
>
&
nva
)
{
size_t
idx
=
0
;
for
(
auto
&
nv
:
nva
)
{
http2
::
index_header
(
stream
->
hdidx
,
nv
.
name
,
nv
.
namelen
,
idx
++
);
http2
::
add_header
(
stream
->
headers
,
nv
.
name
,
nv
.
namelen
,
nv
.
value
,
nv
.
valuelen
,
nv
.
flags
&
NGHTTP2_NV_FLAG_NO_INDEX
);
}
...
...
@@ -270,6 +272,8 @@ Stream::Stream(Http2Handler *handler, int32_t stream_id)
wtimer
.
data
=
this
;
headers
.
reserve
(
10
);
http2
::
init_hdidx
(
hdidx
);
}
Stream
::~
Stream
()
{
...
...
@@ -734,13 +738,12 @@ int Http2Handler::submit_non_final_response(const std::string &status,
int
Http2Handler
::
submit_push_promise
(
Stream
*
stream
,
const
std
::
string
&
push_path
)
{
auto
itr
=
std
::
lower_bound
(
std
::
begin
(
stream
->
headers
),
std
::
end
(
stream
->
headers
),
Header
(
":authority"
,
""
));
auto
authority
=
http2
::
get_header
(
stream
->
hdidx
,
http2
::
HD_AUTHORITY
,
stream
->
headers
);
if
(
itr
==
std
::
end
(
stream
->
headers
)
||
(
*
itr
).
name
!=
":authority"
)
{
itr
=
std
::
lower_bound
(
std
::
begin
(
stream
->
headers
),
std
::
end
(
stream
->
headers
),
Header
(
"host"
,
""
)
);
if
(
!
authority
)
{
authority
=
http2
::
get_header
(
stream
->
hdidx
,
http2
::
HD_HOST
,
stream
->
headers
);
}
auto
nva
=
std
::
vector
<
nghttp2_nv
>
{
...
...
@@ -748,7 +751,7 @@ int Http2Handler::submit_push_promise(Stream *stream,
http2
::
make_nv_ls
(
":path"
,
push_path
),
get_config
()
->
no_tls
?
http2
::
make_nv_ll
(
":scheme"
,
"http"
)
:
http2
::
make_nv_ll
(
":scheme"
,
"https"
),
http2
::
make_nv_ls
(
":authority"
,
(
*
itr
).
value
)};
http2
::
make_nv_ls
(
":authority"
,
authority
->
value
)};
auto
promised_stream_id
=
nghttp2_submit_push_promise
(
session_
,
NGHTTP2_FLAG_END_HEADERS
,
stream
->
stream_id
,
nva
.
data
(),
...
...
@@ -896,10 +899,13 @@ namespace {
void
prepare_redirect_response
(
Stream
*
stream
,
Http2Handler
*
hd
,
const
std
::
string
&
path
,
const
std
::
string
&
status
)
{
auto
scheme
=
http2
::
get_unique_header
(
stream
->
headers
,
":scheme"
);
auto
authority
=
http2
::
get_unique_header
(
stream
->
headers
,
":authority"
);
auto
scheme
=
http2
::
get_header
(
stream
->
hdidx
,
http2
::
HD_SCHEME
,
stream
->
headers
);
auto
authority
=
http2
::
get_header
(
stream
->
hdidx
,
http2
::
HD_AUTHORITY
,
stream
->
headers
);
if
(
!
authority
)
{
authority
=
http2
::
get_unique_header
(
stream
->
headers
,
"host"
);
authority
=
http2
::
get_header
(
stream
->
hdidx
,
http2
::
HD_HOST
,
stream
->
headers
);
}
auto
redirect_url
=
scheme
->
value
;
...
...
@@ -918,17 +924,15 @@ void prepare_response(Stream *stream, Http2Handler *hd,
bool
allow_push
=
true
)
{
int
rv
;
auto
reqpath
=
(
*
std
::
lower_bound
(
std
::
begin
(
stream
->
headers
),
std
::
end
(
stream
->
headers
),
Header
(
":path"
,
""
))).
value
;
http2
::
get_header
(
stream
->
hdidx
,
http2
::
HD_PATH
,
stream
->
headers
)
->
value
;
auto
ims
=
std
::
lower_bound
(
std
::
begin
(
stream
->
headers
),
std
::
end
(
stream
->
headers
),
Header
(
"if-modified-since"
,
""
));
get_header
(
stream
->
hdidx
,
http2
::
HD_IF_MODIFIED_SINCE
,
stream
->
headers
);
time_t
last_mod
=
0
;
bool
last_mod_found
=
false
;
if
(
ims
!=
std
::
end
(
stream
->
headers
)
&&
(
*
ims
).
name
==
"if-modified-since"
)
{
if
(
ims
)
{
last_mod_found
=
true
;
last_mod
=
util
::
parse_http_date
(
(
*
ims
).
value
);
last_mod
=
util
::
parse_http_date
(
ims
->
value
);
}
auto
query_pos
=
reqpath
.
find
(
"?"
);
std
::
string
url
;
...
...
@@ -1011,10 +1015,6 @@ void prepare_response(Stream *stream, Http2Handler *hd,
}
}
// namespace
namespace
{
const
char
*
REQUIRED_HEADERS
[]
=
{
":method"
,
":path"
,
":scheme"
,
nullptr
};
}
// namespace
namespace
{
int
on_header_callback
(
nghttp2_session
*
session
,
const
nghttp2_frame
*
frame
,
const
uint8_t
*
name
,
size_t
namelen
,
...
...
@@ -1041,7 +1041,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if
(
namelen
>
0
&&
name
[
0
]
==
':'
)
{
if
((
!
stream
->
headers
.
empty
()
&&
stream
->
headers
.
back
().
name
.
c_str
()[
0
]
!=
':'
)
||
!
http2
::
check_http2_request_pseudo_header
(
name
,
namelen
))
{
!
http2
::
check_http2_request_pseudo_header
(
stream
->
hdidx
,
name
,
namelen
))
{
nghttp2_submit_rst_stream
(
session
,
NGHTTP2_FLAG_NONE
,
frame
->
hd
.
stream_id
,
NGHTTP2_PROTOCOL_ERROR
);
...
...
@@ -1049,6 +1050,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
}
}
http2
::
index_header
(
stream
->
hdidx
,
name
,
namelen
,
stream
->
headers
.
size
());
http2
::
add_header
(
stream
->
headers
,
name
,
namelen
,
value
,
valuelen
,
flags
&
NGHTTP2_NV_FLAG_NO_INDEX
);
return
0
;
...
...
@@ -1110,27 +1113,13 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if
(
frame
->
headers
.
cat
==
NGHTTP2_HCAT_REQUEST
)
{
http2
::
normalize_headers
(
stream
->
headers
);
if
(
!
http2
::
check_http2_request_headers
(
stream
->
headers
))
{
hd
->
submit_rst_stream
(
stream
,
NGHTTP2_PROTOCOL_ERROR
);
return
0
;
}
for
(
size_t
i
=
0
;
REQUIRED_HEADERS
[
i
];
++
i
)
{
if
(
!
http2
::
get_unique_header
(
stream
->
headers
,
REQUIRED_HEADERS
[
i
]))
{
hd
->
submit_rst_stream
(
stream
,
NGHTTP2_PROTOCOL_ERROR
);
return
0
;
}
}
// intermediary translating from HTTP/1 request to HTTP/2 may
// not produce :authority header field. In this case, it should
// provide host HTTP/1.1 header field.
if
(
!
http2
::
get_unique_header
(
stream
->
headers
,
":authority"
)
&&
!
http2
::
get_unique_header
(
stream
->
headers
,
"host"
))
{
if
(
!
http2
::
check_http2_request_headers
(
stream
->
hdidx
))
{
hd
->
submit_rst_stream
(
stream
,
NGHTTP2_PROTOCOL_ERROR
);
return
0
;
}
auto
expect100
=
http2
::
get_header
(
stream
->
headers
,
"expect"
);
auto
expect100
=
http2
::
get_header
(
stream
->
hdidx
,
http2
::
HD_EXPECT
,
stream
->
headers
);
if
(
expect100
&&
util
::
strieq
(
"100-continue"
,
expect100
->
value
.
c_str
()))
{
hd
->
submit_non_final_response
(
"100"
,
frame
->
hd
.
stream_id
);
...
...
src/HttpServer.h
View file @
8e3406ad
...
...
@@ -83,6 +83,7 @@ struct Stream {
int64_t
body_left
;
int32_t
stream_id
;
int
file
;
int
hdidx
[
http2
::
HD_MAXIDX
];
Stream
(
Http2Handler
*
handler
,
int32_t
stream_id
);
~
Stream
();
};
...
...
src/http2.cc
View file @
8e3406ad
...
...
@@ -572,6 +572,204 @@ int parse_http_status_code(const std::string &src) {
return
status
;
}
void
init_hdidx
(
int
*
hdidx
)
{
memset
(
hdidx
,
-
1
,
sizeof
(
hdidx
[
0
])
*
HD_MAXIDX
);
}
// This function was generated by genheaderfunc.py. Inspired by h2o
// header lookup. https://github.com/h2o/h2o
void
index_header
(
int
*
hdidx
,
const
uint8_t
*
s
,
size_t
len
,
size_t
idx
)
{
switch
(
len
)
{
case
2
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'e'
:
if
(
util
::
strieq
(
"t"
,
s
,
1
))
{
hdidx
[
HD_TE
]
=
idx
;
return
;
}
break
;
}
break
;
case
4
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
't'
:
if
(
util
::
strieq
(
"hos"
,
s
,
3
))
{
hdidx
[
HD_HOST
]
=
idx
;
return
;
}
break
;
}
break
;
case
5
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'h'
:
if
(
util
::
strieq
(
":pat"
,
s
,
4
))
{
hdidx
[
HD_PATH
]
=
idx
;
return
;
}
break
;
}
break
;
case
6
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
't'
:
if
(
util
::
strieq
(
"expec"
,
s
,
5
))
{
hdidx
[
HD_EXPECT
]
=
idx
;
return
;
}
break
;
}
break
;
case
7
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'd'
:
if
(
util
::
strieq
(
":metho"
,
s
,
6
))
{
hdidx
[
HD_METHOD
]
=
idx
;
return
;
}
break
;
case
'e'
:
if
(
util
::
strieq
(
":schem"
,
s
,
6
))
{
hdidx
[
HD_SCHEME
]
=
idx
;
return
;
}
if
(
util
::
strieq
(
"upgrad"
,
s
,
6
))
{
hdidx
[
HD_UPGRADE
]
=
idx
;
return
;
}
break
;
}
break
;
case
10
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'e'
:
if
(
util
::
strieq
(
"keep-aliv"
,
s
,
9
))
{
hdidx
[
HD_KEEP_ALIVE
]
=
idx
;
return
;
}
break
;
case
'n'
:
if
(
util
::
strieq
(
"connectio"
,
s
,
9
))
{
hdidx
[
HD_CONNECTION
]
=
idx
;
return
;
}
break
;
case
'y'
:
if
(
util
::
strieq
(
":authorit"
,
s
,
9
))
{
hdidx
[
HD_AUTHORITY
]
=
idx
;
return
;
}
break
;
}
break
;
case
16
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'n'
:
if
(
util
::
strieq
(
"proxy-connectio"
,
s
,
15
))
{
hdidx
[
HD_PROXY_CONNECTION
]
=
idx
;
return
;
}
break
;
}
break
;
case
17
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'e'
:
if
(
util
::
strieq
(
"if-modified-sinc"
,
s
,
16
))
{
hdidx
[
HD_IF_MODIFIED_SINCE
]
=
idx
;
return
;
}
break
;
case
'g'
:
if
(
util
::
strieq
(
"transfer-encodin"
,
s
,
16
))
{
hdidx
[
HD_TRANSFER_ENCODING
]
=
idx
;
return
;
}
break
;
}
break
;
}
}
bool
check_http2_request_pseudo_header
(
int
*
hdidx
,
const
uint8_t
*
s
,
size_t
len
)
{
switch
(
len
)
{
case
5
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'h'
:
if
(
util
::
strieq
(
":pat"
,
s
,
4
))
{
if
(
hdidx
[
HD_PATH
]
!=
-
1
)
{
return
false
;
}
return
true
;
}
break
;
}
break
;
case
7
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'd'
:
if
(
util
::
strieq
(
":metho"
,
s
,
6
))
{
if
(
hdidx
[
HD_METHOD
]
!=
-
1
)
{
return
false
;
}
return
true
;
}
break
;
case
'e'
:
if
(
util
::
strieq
(
":schem"
,
s
,
6
))
{
if
(
hdidx
[
HD_SCHEME
]
!=
-
1
)
{
return
false
;
}
return
true
;
}
break
;
}
break
;
case
10
:
switch
(
util
::
lowcase
(
s
[
len
-
1
]))
{
case
'y'
:
if
(
util
::
strieq
(
":authorit"
,
s
,
9
))
{
if
(
hdidx
[
HD_AUTHORITY
]
!=
-
1
)
{
return
false
;
}
return
true
;
}
break
;
}
break
;
}
return
false
;
}
bool
check_http2_headers
(
int
*
hdidx
)
{
if
(
hdidx
[
HD_CONNECTION
]
!=
-
1
||
hdidx
[
HD_KEEP_ALIVE
]
!=
-
1
||
hdidx
[
HD_PROXY_CONNECTION
]
!=
-
1
||
hdidx
[
HD_TE
]
!=
-
1
||
hdidx
[
HD_TRANSFER_ENCODING
]
!=
-
1
||
hdidx
[
HD_UPGRADE
]
!=
-
1
)
{
return
false
;
}
return
true
;
}
bool
check_http2_request_headers
(
int
*
hdidx
)
{
if
(
!
check_http2_headers
(
hdidx
))
{
return
false
;
}
if
(
hdidx
[
HD_METHOD
]
==
-
1
||
hdidx
[
HD_PATH
]
==
-
1
||
hdidx
[
HD_SCHEME
]
==
-
1
||
(
hdidx
[
HD_AUTHORITY
]
==
-
1
&&
hdidx
[
HD_HOST
]
==
-
1
))
{
return
false
;
}
return
true
;
}
const
Headers
::
value_type
*
get_header
(
int
*
hdidx
,
int
hdkey
,
const
Headers
&
nva
)
{
auto
i
=
hdidx
[
hdkey
];
if
(
i
==
-
1
)
{
return
nullptr
;
}
return
&
nva
[
i
];
}
}
// namespace http2
}
// namespace nghttp2
src/http2.h
View file @
8e3406ad
...
...
@@ -218,6 +218,45 @@ int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
// Returns parsed HTTP status code. Returns -1 on failure.
int
parse_http_status_code
(
const
std
::
string
&
src
);
enum
{
HD_AUTHORITY
,
HD_METHOD
,
HD_PATH
,
HD_SCHEME
,
HD_CONNECTION
,
HD_EXPECT
,
HD_HOST
,
HD_IF_MODIFIED_SINCE
,
HD_KEEP_ALIVE
,
HD_PROXY_CONNECTION
,
HD_TE
,
HD_TRANSFER_ENCODING
,
HD_UPGRADE
,
HD_MAXIDX
,
};
// Initializes |hdidx|, header index. The |hdidx| must point to the
// array containing at least HD_MAXIDX elements.
void
init_hdidx
(
int
*
hdidx
);
// Indexes header |name| of length |namelen| using index |idx|.
void
index_header
(
int
*
hdidx
,
const
uint8_t
*
name
,
size_t
namelen
,
size_t
idx
);
// Checks pseudo header |name| of length |namelen| are unique and
// allowed for HTTP/2 request.
bool
check_http2_request_pseudo_header
(
int
*
hdidx
,
const
uint8_t
*
name
,
size_t
namelen
);
// Checks |hdidx| does not contain disallowed headers.
bool
check_http2_headers
(
int
*
hdidx
);
// Checks |hdidx| does not contain disallowed headers and contains
// mandatory headers. This funtions internally calls
// check_http2_headers().
bool
check_http2_request_headers
(
int
*
hdidx
);
// Returns header denoted by |hdkey| using index |hdidx|.
const
Headers
::
value_type
*
get_header
(
int
*
hdidx
,
int
hdkey
,
const
Headers
&
nva
);
}
// namespace http2
}
// namespace nghttp2
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment