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
19d28365
Commit
19d28365
authored
Mar 11, 2018
by
Tatsuhiro Tsujikawa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nghttpx: Add experimental websocket with extended CONNECT
parent
a26eb08a
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
103 additions
and
22 deletions
+103
-22
genheaderfunc.py
genheaderfunc.py
+2
-0
src/http2.cc
src/http2.cc
+21
-1
src/http2.h
src/http2.h
+4
-0
src/shrpx_client_handler.cc
src/shrpx_client_handler.cc
+1
-1
src/shrpx_downstream.cc
src/shrpx_downstream.cc
+6
-1
src/shrpx_downstream.h
src/shrpx_downstream.h
+17
-0
src/shrpx_http2_downstream_connection.cc
src/shrpx_http2_downstream_connection.cc
+4
-4
src/shrpx_http2_upstream.cc
src/shrpx_http2_upstream.cc
+36
-9
src/shrpx_http_downstream_connection.cc
src/shrpx_http_downstream_connection.cc
+12
-6
No files found.
genheaderfunc.py
View file @
19d28365
...
...
@@ -9,6 +9,7 @@ HEADERS = [
':scheme'
,
':status'
,
':host'
,
# for spdy
':protocol'
,
'expect'
,
'host'
,
'if-modified-since'
,
...
...
@@ -31,6 +32,7 @@ HEADERS = [
"user-agent"
,
"date"
,
"content-type"
,
"sec-websocket-accept"
,
# disallowed h1 headers
'connection'
,
'keep-alive'
,
...
...
src/http2.cc
View file @
19d28365
...
...
@@ -438,6 +438,11 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
kv
=
&
(
*
it_via
);
it_via
=
it
;
break
;
case
HD_SEC_WEBSOCKET_ACCEPT
:
if
(
flags
&
HDOP_STRIP_SEC_WEBSOCKET_ACCEPT
)
{
continue
;
}
break
;
}
nva
.
push_back
(
make_nv_internal
(
kv
->
name
,
kv
->
value
,
kv
->
no_index
,
nv_flags
));
...
...
@@ -821,6 +826,11 @@ int lookup_token(const uint8_t *name, size_t namelen) {
return
HD_FORWARDED
;
}
break
;
case
'l'
:
if
(
util
::
streq_l
(
":protoco"
,
name
,
8
))
{
return
HD__PROTOCOL
;
}
break
;
}
break
;
case
10
:
...
...
@@ -926,6 +936,15 @@ int lookup_token(const uint8_t *name, size_t namelen) {
break
;
}
break
;
case
20
:
switch
(
name
[
19
])
{
case
't'
:
if
(
util
::
streq_l
(
"sec-websocket-accep"
,
name
,
19
))
{
return
HD_SEC_WEBSOCKET_ACCEPT
;
}
break
;
}
break
;
}
return
-
1
;
}
...
...
@@ -1313,7 +1332,8 @@ std::string path_join(const StringRef &base_path, const StringRef &base_query,
}
bool
expect_response_body
(
int
status_code
)
{
return
status_code
/
100
!=
1
&&
status_code
!=
304
&&
status_code
!=
204
;
return
status_code
==
101
||
(
status_code
/
100
!=
1
&&
status_code
!=
304
&&
status_code
!=
204
);
}
bool
expect_response_body
(
const
std
::
string
&
method
,
int
status_code
)
{
...
...
src/http2.h
View file @
19d28365
...
...
@@ -206,6 +206,8 @@ enum HeaderBuildOp {
// Strip above all header fields.
HDOP_STRIP_ALL
=
HDOP_STRIP_FORWARDED
|
HDOP_STRIP_X_FORWARDED_FOR
|
HDOP_STRIP_X_FORWARDED_PROTO
|
HDOP_STRIP_VIA
,
// Sec-WebSocket-Accept header field must be stripped.
HDOP_STRIP_SEC_WEBSOCKET_ACCEPT
=
1
<<
4
,
};
// Appends headers in |headers| to |nv|. |headers| must be indexed
...
...
@@ -293,6 +295,7 @@ enum {
HD__HOST
,
HD__METHOD
,
HD__PATH
,
HD__PROTOCOL
,
HD__SCHEME
,
HD__STATUS
,
HD_ACCEPT_ENCODING
,
...
...
@@ -313,6 +316,7 @@ enum {
HD_LINK
,
HD_LOCATION
,
HD_PROXY_CONNECTION
,
HD_SEC_WEBSOCKET_ACCEPT
,
HD_SERVER
,
HD_TE
,
HD_TRAILER
,
...
...
src/shrpx_client_handler.cc
View file @
19d28365
...
...
@@ -978,7 +978,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
StringRef
path
;
// CONNECT method does not have path. But we requires path in
// host-path mapping. As workaround, we assume that path is "/".
if
(
req
.
method
!=
HTTP_CONNECT
)
{
if
(
!
req
.
regular_connect_method
()
)
{
path
=
req
.
path
;
}
...
...
src/shrpx_downstream.cc
View file @
19d28365
...
...
@@ -722,7 +722,12 @@ bool Downstream::validate_response_recv_body_length() const {
void
Downstream
::
check_upgrade_fulfilled
()
{
if
(
req_
.
method
==
HTTP_CONNECT
)
{
upgraded_
=
200
<=
resp_
.
http_status
&&
resp_
.
http_status
<
300
;
if
(
req_
.
connect_proto
)
{
// TODO For websocket, check Sec-WebSocket-Accept header field.
upgraded_
=
resp_
.
http_status
==
101
;
}
else
{
upgraded_
=
200
<=
resp_
.
http_status
&&
resp_
.
http_status
<
300
;
}
return
;
}
...
...
src/shrpx_downstream.h
View file @
19d28365
...
...
@@ -134,6 +134,12 @@ private:
bool
trailer_key_prev_
;
};
// Protocols allowed in HTTP/2 :protocol header field.
enum
shrpx_connect_proto
{
CONNECT_PROTO_NONE
,
CONNECT_PROTO_WEBSOCKET
,
};
struct
Request
{
Request
(
BlockAllocator
&
balloc
)
:
fs
(
balloc
,
16
),
...
...
@@ -153,6 +159,14 @@ struct Request {
unconsumed_body_length
-=
len
;
}
bool
regular_connect_method
()
const
{
return
method
==
HTTP_CONNECT
&&
!
connect_proto
;
}
bool
extended_connect_method
()
const
{
return
method
==
HTTP_CONNECT
&&
connect_proto
;
}
FieldStore
fs
;
// Timestamp when all request header fields are received.
std
::
shared_ptr
<
Timestamp
>
tstamp
;
...
...
@@ -176,6 +190,9 @@ struct Request {
int
method
;
// HTTP major and minor version
int
http_major
,
http_minor
;
// connect_protocol specified in HTTP/2 :protocol pseudo header
// field which enables extended CONNECT method.
int
connect_proto
;
// Returns true if the request is HTTP upgrade (HTTP Upgrade or
// CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2
// Upgrade, check get_http2_upgrade_request().
...
...
src/shrpx_http2_downstream_connection.cc
View file @
19d28365
...
...
@@ -250,7 +250,7 @@ int Http2DownstreamConnection::push_request_headers() {
auto
&
http2conf
=
config
->
http2
;
auto
no_host_rewrite
=
httpconf
.
no_host_rewrite
||
config
->
http2_proxy
||
req
.
method
==
HTTP_CONNECT
;
req
.
regular_connect_method
()
;
// http2session_ has already in CONNECTED state, so we can get
// addr_idx here.
...
...
@@ -288,7 +288,7 @@ int Http2DownstreamConnection::push_request_headers() {
nva
.
push_back
(
http2
::
make_nv_ls_nocopy
(
":method"
,
http2
::
to_method_string
(
req
.
method
)));
if
(
req
.
method
!=
HTTP_CONNECT
)
{
if
(
!
req
.
regular_connect_method
()
)
{
assert
(
!
req
.
scheme
.
empty
());
auto
addr
=
http2session_
->
get_addr
();
...
...
@@ -339,7 +339,7 @@ int Http2DownstreamConnection::push_request_headers() {
if
(
fwdconf
.
params
)
{
auto
params
=
fwdconf
.
params
;
if
(
config
->
http2_proxy
||
req
.
method
==
HTTP_CONNECT
)
{
if
(
config
->
http2_proxy
||
req
.
regular_connect_method
()
)
{
params
&=
~
FORWARDED_PROTO
;
}
...
...
@@ -380,7 +380,7 @@ int Http2DownstreamConnection::push_request_headers() {
nva
.
push_back
(
http2
::
make_nv_ls_nocopy
(
"x-forwarded-for"
,
xff
->
value
));
}
if
(
!
config
->
http2_proxy
&&
req
.
method
!=
HTTP_CONNECT
)
{
if
(
!
config
->
http2_proxy
&&
!
req
.
regular_connect_method
()
)
{
auto
xfp
=
xfpconf
.
strip_incoming
?
nullptr
:
req
.
fs
.
header
(
http2
::
HD_X_FORWARDED_PROTO
);
...
...
src/shrpx_http2_upstream.cc
View file @
19d28365
...
...
@@ -391,6 +391,19 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
}
}
if
(
!
config
->
http2_proxy
)
{
auto
connect_proto
=
req
.
fs
.
header
(
http2
::
HD__PROTOCOL
);
if
(
connect_proto
)
{
if
(
connect_proto
->
value
!=
"websocket"
)
{
if
(
error_reply
(
downstream
,
400
)
!=
0
)
{
return
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
;
}
return
0
;
}
req
.
connect_proto
=
CONNECT_PROTO_WEBSOCKET
;
}
}
if
(
!
(
frame
->
hd
.
flags
&
NGHTTP2_FLAG_END_STREAM
))
{
req
.
http2_expect_body
=
true
;
}
else
if
(
req
.
fs
.
content_length
==
-
1
)
{
...
...
@@ -1001,8 +1014,8 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
flow_control_
=
true
;
// TODO Maybe call from outside?
std
::
array
<
nghttp2_settings_entry
,
3
>
entry
;
size_t
nentry
=
2
;
std
::
array
<
nghttp2_settings_entry
,
4
>
entry
;
size_t
nentry
=
3
;
entry
[
0
].
settings_id
=
NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
;
entry
[
0
].
value
=
http2conf
.
upstream
.
max_concurrent_streams
;
...
...
@@ -1014,6 +1027,9 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
entry
[
1
].
value
=
http2conf
.
upstream
.
window_size
;
}
entry
[
2
].
settings_id
=
NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL
;
entry
[
2
].
value
=
1
;
if
(
http2conf
.
upstream
.
decoder_dynamic_table_size
!=
NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
)
{
entry
[
nentry
].
settings_id
=
NGHTTP2_SETTINGS_HEADER_TABLE_SIZE
;
...
...
@@ -1662,11 +1678,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
nva
.
reserve
(
resp
.
fs
.
headers
().
size
()
+
5
+
httpconf
.
add_response_headers
.
size
());
auto
response_status
=
http2
::
stringify_status
(
balloc
,
resp
.
http_status
);
if
(
downstream
->
get_non_final_response
())
{
auto
response_status
=
http2
::
stringify_status
(
balloc
,
resp
.
http_status
);
nva
.
push_back
(
http2
::
make_nv_ls_nocopy
(
":status"
,
response_status
));
nva
.
push_back
(
http2
::
make_nv_ls_nocopy
(
":status"
,
response_status
));
if
(
downstream
->
get_non_final_response
())
{
http2
::
copy_headers_to_nva_nocopy
(
nva
,
resp
.
fs
.
headers
(),
http2
::
HDOP_STRIP_ALL
);
...
...
@@ -1688,8 +1704,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
return
0
;
}
http2
::
copy_headers_to_nva_nocopy
(
nva
,
resp
.
fs
.
headers
(),
http2
::
HDOP_STRIP_ALL
&
~
http2
::
HDOP_STRIP_VIA
);
auto
striphd_flags
=
http2
::
HDOP_STRIP_ALL
&
~
http2
::
HDOP_STRIP_VIA
;
StringRef
response_status
;
if
(
req
.
connect_proto
&&
resp
.
http_status
==
101
)
{
response_status
=
http2
::
stringify_status
(
balloc
,
200
);
striphd_flags
|=
http2
::
HDOP_STRIP_SEC_WEBSOCKET_ACCEPT
;
}
else
{
response_status
=
http2
::
stringify_status
(
balloc
,
resp
.
http_status
);
}
nva
.
push_back
(
http2
::
make_nv_ls_nocopy
(
":status"
,
response_status
));
http2
::
copy_headers_to_nva_nocopy
(
nva
,
resp
.
fs
.
headers
(),
striphd_flags
);
if
(
!
config
->
http2_proxy
&&
!
httpconf
.
no_server_rewrite
)
{
nva
.
push_back
(
http2
::
make_nv_ls_nocopy
(
"server"
,
httpconf
.
server_name
));
...
...
@@ -1700,7 +1727,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
}
}
if
(
req
.
method
!=
HTTP_CONNECT
||
!
downstream
->
get_upgraded
())
{
if
(
!
req
.
regular_connect_method
()
||
!
downstream
->
get_upgraded
())
{
auto
affinity_cookie
=
downstream
->
get_affinity_cookie_to_send
();
if
(
affinity_cookie
)
{
auto
dconn
=
downstream
->
get_downstream_connection
();
...
...
@@ -1874,7 +1901,7 @@ int Http2Upstream::on_downstream_abort_request_with_https_redirect(
int
Http2Upstream
::
redirect_to_https
(
Downstream
*
downstream
)
{
auto
&
req
=
downstream
->
request
();
if
(
req
.
method
==
HTTP_CONNECT
||
req
.
scheme
!=
"http"
)
{
if
(
req
.
regular_connect_method
()
||
req
.
scheme
!=
"http"
)
{
return
error_reply
(
downstream
,
400
);
}
...
...
src/shrpx_http_downstream_connection.cc
View file @
19d28365
...
...
@@ -484,7 +484,7 @@ int HttpDownstreamConnection::push_request_headers() {
auto
&
balloc
=
downstream_
->
get_block_allocator
();
auto
connect_method
=
req
.
method
==
HTTP_CONNECT
;
auto
connect_method
=
req
.
regular_connect_method
()
;
auto
config
=
get_config
();
auto
&
httpconf
=
config
->
http
;
...
...
@@ -508,7 +508,8 @@ int HttpDownstreamConnection::push_request_headers() {
auto
buf
=
downstream_
->
get_request_buf
();
// Assume that method and request path do not contain \r\n.
auto
meth
=
http2
::
to_method_string
(
req
.
method
);
auto
meth
=
http2
::
to_method_string
(
req
.
connect_proto
==
CONNECT_PROTO_WEBSOCKET
?
HTTP_GET
:
req
.
method
);
buf
->
append
(
meth
);
buf
->
append
(
' '
);
...
...
@@ -552,7 +553,8 @@ int HttpDownstreamConnection::push_request_headers() {
// set transfer-encoding only when content-length is unknown and
// request body is expected.
if
(
!
connect_method
&&
req
.
http2_expect_body
&&
req
.
fs
.
content_length
==
-
1
)
{
if
(
req
.
method
!=
HTTP_CONNECT
&&
req
.
http2_expect_body
&&
req
.
fs
.
content_length
==
-
1
)
{
downstream_
->
set_chunked_request
(
true
);
buf
->
append
(
"Transfer-Encoding: chunked
\r\n
"
);
}
...
...
@@ -561,7 +563,11 @@ int HttpDownstreamConnection::push_request_headers() {
buf
->
append
(
"Connection: close
\r\n
"
);
}
if
(
!
connect_method
&&
req
.
upgrade_request
)
{
if
(
req
.
connect_proto
==
CONNECT_PROTO_WEBSOCKET
)
{
// TODO Generate Sec-WebSocket-Key
buf
->
append
(
"Upgrade: websocket
\r\n
Connection: "
"Upgrade
\r\n
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
\r\n
"
);
}
else
if
(
!
connect_method
&&
req
.
upgrade_request
)
{
auto
connection
=
req
.
fs
.
header
(
http2
::
HD_CONNECTION
);
if
(
connection
)
{
buf
->
append
(
"Connection: "
);
...
...
@@ -693,7 +699,7 @@ int HttpDownstreamConnection::push_request_headers() {
// Don't call signal_write() if we anticipate request body. We call
// signal_write() when we received request body chunk, and it
// enables us to send headers and data in one writev system call.
if
(
connect_method
||
if
(
connect_method
||
req
.
connect_proto
||
(
!
req
.
http2_expect_body
&&
req
.
fs
.
content_length
==
0
))
{
signal_write
();
}
...
...
@@ -901,7 +907,7 @@ int htp_hdrs_completecb(http_parser *htp) {
return
-
1
;
}
}
else
if
(
resp
.
http_status
/
100
==
1
||
(
resp
.
http_status
==
200
&&
req
.
method
==
HTTP_CONNECT
))
{
(
resp
.
http_status
==
200
&&
req
.
regular_connect_method
()
))
{
if
(
resp
.
fs
.
header
(
http2
::
HD_CONTENT_LENGTH
)
||
resp
.
fs
.
header
(
http2
::
HD_TRANSFER_ENCODING
))
{
return
-
1
;
...
...
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