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
5ff73de1
Commit
5ff73de1
authored
Sep 13, 2014
by
Tatsuhiro Tsujikawa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
libevent-server: Use nghttp2_option_set_recv_client_preface()
parent
901de5fb
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
72 additions
and
104 deletions
+72
-104
doc/sources/tutorial-server.rst
doc/sources/tutorial-server.rst
+50
-67
examples/libevent-server.c
examples/libevent-server.c
+22
-37
No files found.
doc/sources/tutorial-server.rst
View file @
5ff73de1
...
...
@@ -79,7 +79,6 @@ We use the ``http2_session_data`` structure to store session-level
app_context *app_ctx;
nghttp2_session *session;
char *client_addr;
size_t handshake_leftlen;
} http2_session_data;
We use the ``http2_stream_data`` structure to store stream-level data::
...
...
@@ -91,17 +90,15 @@ We use the ``http2_stream_data`` structure to store stream-level data::
int fd;
} http2_stream_data;
A single HTTP/2 session can have multiple streams. We manage these multiple
streams with a doubly linked list. The first element of this list is pointed
to by the ``root->next`` in ``http2_session_data``. Initially, ``root->next``
is ``NULL``. The ``handshake_leftlen`` member of ``http2_session_data`` is
used to track the number of bytes remaining when receiving the first client
connection preface (:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`), which is a 24
bytes long magic string from the client. We use libevent's bufferevent
structure to perform network I/O. Note that the bufferevent object is kept in
``http2_session_data`` and not in ``http2_stream_data``. This is because
``http2_stream_data`` is just a logical stream multiplexed over the single
connection managed by bufferevent in ``http2_session_data``.
A single HTTP/2 session can have multiple streams. We manage these
multiple streams with a doubly linked list. The first element of this
list is pointed to by the ``root->next`` in ``http2_session_data``.
Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent
structure to perform network I/O. Note that the bufferevent object is
kept in ``http2_session_data`` and not in ``http2_stream_data``. This
is because ``http2_stream_data`` is just a logical stream multiplexed
over the single connection managed by bufferevent in
``http2_session_data``.
We first create a listener object to accept incoming connections. We use
libevent's ``struct evconnlistener`` for this purpose::
...
...
@@ -148,13 +145,14 @@ accepted::
http2_session_data *session_data;
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
session_data);
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb,
session_data);
}
Here we create the ``http2_session_data`` object. The bufferevent for this
connection is also initialized at this time. We specify two callbacks for the
bufferevent: ``handshake_readcb`` and ``eventcb``.
Here we create the ``http2_session_data`` object. The bufferevent for
this connection is also initialized at this time. We specify three
callbacks for the bufferevent: ``readcb``, ``writecb`` and
``eventcb``.
The ``eventcb()`` callback is invoked by the libevent event loop when an event
(e.g., connection has been established, timeout, etc) happens on the
...
...
@@ -165,6 +163,14 @@ underlying network socket::
http2_session_data *session_data = (http2_session_data*)ptr;
if(events & BEV_EVENT_CONNECTED) {
fprintf(stderr, "%s connected\n", session_data->client_addr);
initialize_nghttp2_session(session_data);
if(send_server_connection_header(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
return;
}
if(events & BEV_EVENT_EOF) {
...
...
@@ -177,60 +183,29 @@ underlying network socket::
delete_http2_session_data(session_data);
}
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
events, we just simply tear down the connection. The
``delete_http2_session_data()`` function destroys the ``http2_session_data``
object and thus also its bufferevent member. As a result, the underlying
connection is closed. The ``BEV_EVENT_CONNECTED`` event is invoked when
SSL/TLS handshake is finished successfully.
``handshake_readcb()`` is a callback function to handle a 24 bytes magic byte
string coming from a client, since the nghttp2 library does not handle it::
static void handshake_readcb(struct bufferevent *bev, void *ptr)
{
http2_session_data *session_data = (http2_session_data*)ptr;
uint8_t data[24];
struct evbuffer *input = bufferevent_get_input(session_data->bev);
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
const char *conhead = NGHTTP2_CLIENT_CONNECTION_PREFACE;
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
- session_data->handshake_leftlen, data, readlen) != 0) {
delete_http2_session_data(session_data);
return;
}
session_data->handshake_leftlen -= readlen;
if(session_data->handshake_leftlen == 0) {
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
/* Process pending data in buffer since they are not notified
further */
initialize_nghttp2_session(session_data);
if(send_server_connection_header(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
if(session_recv(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
}
}
We check that the received byte string matches
:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`. When they match, the connection
state is ready to start the HTTP/2 communication. First we change the callback
functions for the bufferevent object. We use the same ``eventcb`` callback as
before, but we specify new ``readcb`` and ``writecb`` functions to handle the
HTTP/2 communication. These two functions are described later.
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
The ``delete_http2_session_data()`` function destroys the
``http2_session_data`` object and thus also its bufferevent member.
As a result, the underlying connection is closed. The
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
finished successfully. Now we are ready to start the HTTP/2
communication.
We initialize a nghttp2 session object which is done in
``initialize_nghttp2_session()``::
static void initialize_nghttp2_session(http2_session_data *session_data)
{
nghttp2_option *option;
nghttp2_session_callbacks *callbacks;
nghttp2_option_new(&option);
/* Tells nghttp2_session object that it handles client connection
preface */
nghttp2_option_set_recv_client_preface(option, 1);
nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
...
...
@@ -247,14 +222,22 @@ We initialize a nghttp2 session object which is done in
nghttp2_session_callbacks_set_on_begin_headers_callback
(callbacks, on_begin_headers_callback);
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
option);
nghttp2_session_callbacks_del(callbacks);
nghttp2_option_del(option);
}
Since we are creating a server, the nghttp2 session object is created using
`nghttp2_session_server_new()` function. We registers five callbacks for
nghttp2 session object. We'll talk about these callbacks later.
Since we are creating a server and uses options, the nghttp2 session
object is created using `nghttp2_session_server_new2()` function. We
registers five callbacks for nghttp2 session object. We'll talk about
these callbacks later. Our server only speaks HTTP/2. In this case,
we use `nghttp2_option_set_recv_client_preface()` to make
:type:`nghttp2_session` object handle client connection preface, which
saves some lines of application code.
After initialization of the nghttp2 session object, we are going to send
a server connection header in ``send_server_connection_header()``::
...
...
examples/libevent-server.c
View file @
5ff73de1
...
...
@@ -69,7 +69,6 @@ typedef struct http2_session_data {
app_context
*
app_ctx
;
nghttp2_session
*
session
;
char
*
client_addr
;
size_t
handshake_leftlen
;
}
http2_session_data
;
struct
app_context
{
...
...
@@ -192,7 +191,6 @@ static http2_session_data* create_http2_session_data(app_context *app_ctx,
(
app_ctx
->
evbase
,
fd
,
ssl
,
BUFFEREVENT_SSL_ACCEPTING
,
BEV_OPT_CLOSE_ON_FREE
|
BEV_OPT_DEFER_CALLBACKS
);
session_data
->
handshake_leftlen
=
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
;
rv
=
getnameinfo
(
addr
,
addrlen
,
host
,
sizeof
(
host
),
NULL
,
0
,
NI_NUMERICHOST
);
if
(
rv
!=
0
)
{
session_data
->
client_addr
=
strdup
(
"(unknown)"
);
...
...
@@ -555,8 +553,15 @@ static int on_stream_close_callback(nghttp2_session *session,
static
void
initialize_nghttp2_session
(
http2_session_data
*
session_data
)
{
nghttp2_option
*
option
;
nghttp2_session_callbacks
*
callbacks
;
nghttp2_option_new
(
&
option
);
/* Tells nghttp2_session object that it handles client connection
preface */
nghttp2_option_set_recv_client_preface
(
option
,
1
);
nghttp2_session_callbacks_new
(
&
callbacks
);
nghttp2_session_callbacks_set_send_callback
(
callbacks
,
send_callback
);
...
...
@@ -573,9 +578,13 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
nghttp2_session_callbacks_set_on_begin_headers_callback
(
callbacks
,
on_begin_headers_callback
);
nghttp2_session_server_new
(
&
session_data
->
session
,
callbacks
,
session_data
);
nghttp2_session_server_new2
(
&
session_data
->
session
,
callbacks
,
session_data
,
option
);
nghttp2_session_callbacks_del
(
callbacks
);
nghttp2_option_del
(
option
);
}
/* Send HTTP/2 client connection header, which includes 24 bytes
...
...
@@ -638,6 +647,14 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
http2_session_data
*
session_data
=
(
http2_session_data
*
)
ptr
;
if
(
events
&
BEV_EVENT_CONNECTED
)
{
fprintf
(
stderr
,
"%s connected
\n
"
,
session_data
->
client_addr
);
initialize_nghttp2_session
(
session_data
);
if
(
send_server_connection_header
(
session_data
)
!=
0
)
{
delete_http2_session_data
(
session_data
);
return
;
}
return
;
}
if
(
events
&
BEV_EVENT_EOF
)
{
...
...
@@ -650,38 +667,6 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
delete_http2_session_data
(
session_data
);
}
/* readcb for bufferevent to check first 24 bytes client connection
header. */
static
void
handshake_readcb
(
struct
bufferevent
*
bev
,
void
*
ptr
)
{
http2_session_data
*
session_data
=
(
http2_session_data
*
)
ptr
;
uint8_t
data
[
24
];
struct
evbuffer
*
input
=
bufferevent_get_input
(
session_data
->
bev
);
int
readlen
=
evbuffer_remove
(
input
,
data
,
session_data
->
handshake_leftlen
);
const
char
*
conhead
=
NGHTTP2_CLIENT_CONNECTION_PREFACE
;
if
(
memcmp
(
conhead
+
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
-
session_data
->
handshake_leftlen
,
data
,
readlen
)
!=
0
)
{
delete_http2_session_data
(
session_data
);
return
;
}
session_data
->
handshake_leftlen
-=
readlen
;
if
(
session_data
->
handshake_leftlen
==
0
)
{
bufferevent_setcb
(
session_data
->
bev
,
readcb
,
writecb
,
eventcb
,
ptr
);
/* Process pending data in buffer since they are not notified
further */
initialize_nghttp2_session
(
session_data
);
if
(
send_server_connection_header
(
session_data
)
!=
0
)
{
delete_http2_session_data
(
session_data
);
return
;
}
if
(
session_recv
(
session_data
)
!=
0
)
{
delete_http2_session_data
(
session_data
);
return
;
}
}
}
/* callback for evconnlistener */
static
void
acceptcb
(
struct
evconnlistener
*
listener
,
int
fd
,
struct
sockaddr
*
addr
,
int
addrlen
,
void
*
arg
)
...
...
@@ -690,8 +675,8 @@ static void acceptcb(struct evconnlistener *listener, int fd,
http2_session_data
*
session_data
;
session_data
=
create_http2_session_data
(
app_ctx
,
fd
,
addr
,
addrlen
);
bufferevent_setcb
(
session_data
->
bev
,
handshake_readcb
,
NULL
,
eventcb
,
session_data
);
bufferevent_setcb
(
session_data
->
bev
,
readcb
,
writecb
,
eventcb
,
session_data
);
}
static
void
start_listen
(
struct
event_base
*
evbase
,
const
char
*
service
,
...
...
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