1251 lines
145 KiB
HTML
1251 lines
145 KiB
HTML
|
|
|
|
<!DOCTYPE html>
|
|
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
|
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>Tutorial: HTTP/2 client — nghttp2 0.4.0-DEV documentation</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
var DOCUMENTATION_OPTIONS = {
|
|
URL_ROOT:'./',
|
|
VERSION:'0.4.0-DEV',
|
|
COLLAPSE_INDEX:false,
|
|
FILE_SUFFIX:'.html',
|
|
HAS_SOURCE: false
|
|
};
|
|
</script>
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
<script type="text/javascript" src="_static/underscore.js"></script>
|
|
<script type="text/javascript" src="_static/doctools.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
|
<script type="text/javascript" src="_static/js/theme.js"></script>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
jQuery(function () {
|
|
SphinxRtdTheme.StickyNav.enable();
|
|
});
|
|
</script>
|
|
|
|
|
|
|
|
<link rel="top" title="nghttp2 0.4.0-DEV documentation" href="index.html"/>
|
|
<link rel="next" title="Tutorial: HTTP/2 server" href="tutorial-server.html"/>
|
|
<link rel="prev" title="nghttp2 - HTTP/2 C Library" href="package_README.html"/>
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
|
|
|
|
</head>
|
|
|
|
<body class="wy-body-for-nav" role="document">
|
|
|
|
<div class="wy-grid-for-nav">
|
|
|
|
|
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
|
<div class="wy-side-nav-search">
|
|
<a href="index.html" class="fa fa-home"> nghttp2</a>
|
|
<div role="search">
|
|
<form id ="rtd-search-form" class="wy-form" action="search.html" method="get">
|
|
<input type="text" name="q" placeholder="Search docs" />
|
|
<input type="hidden" name="check_keywords" value="yes" />
|
|
<input type="hidden" name="area" value="default" />
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
|
|
|
|
|
<ul class="current">
|
|
<li class="toctree-l1"><a class="reference internal" href="package_README.html">nghttp2 - HTTP/2 C Library</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#development-status">Development Status</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#public-test-server">Public Test Server</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#requirements">Requirements</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#build-from-git">Build from git</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#building-documentation">Building documentation</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#client-server-and-proxy-programs">Client, Server and Proxy programs</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#benchmarking-tool">Benchmarking tool</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#hpack-tools">HPACK tools</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#python-bindings">Python bindings</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1 current"><a class="current reference internal" href="">Tutorial: HTTP/2 client</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="#libevent-client-c">libevent-client.c</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-server.html">Tutorial: HTTP/2 server</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="tutorial-server.html#libevent-server-c">libevent-server.c</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="nghttpx-howto.html">nghttpx - HOW-TO</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#default-mode">Default mode</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#http-2-proxy-mode">HTTP/2 proxy mode</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#client-mode">Client mode</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#client-proxy-mode">Client proxy mode</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#http-2-bridge-mode">HTTP/2 bridge mode</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#disable-ssl-tls">Disable SSL/TLS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#specifying-additional-ca-certificate">Specifying additional CA certificate</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#read-write-rate-limit">Read/write rate limit</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="apiref.html">API Reference</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="apiref.html#includes">Includes</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="apiref.html#remarks">Remarks</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="apiref.html#macros">Macros</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="apiref.html#enums">Enums</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="apiref.html#types-structs-unions-and-typedefs">Types (structs, unions and typedefs)</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="apiref.html#functions">Functions</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="nghttp2.h.html">nghttp2.h</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="nghttp2ver.h.html">nghttp2ver.h</a></li>
|
|
<li class="toctree-l1"><a class="reference external" href="https://github.com/tatsuhiro-t/nghttp2">Source</a></li>
|
|
<li class="toctree-l1"><a class="reference external" href="https://github.com/tatsuhiro-t/nghttp2/issues">Issues</a></li>
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
|
|
|
|
|
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
|
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
|
<a href="index.html">nghttp2</a>
|
|
</nav>
|
|
|
|
|
|
|
|
<div class="wy-nav-content">
|
|
<div class="rst-content">
|
|
<div role="navigation" aria-label="breadcrumbs navigation">
|
|
<ul class="wy-breadcrumbs">
|
|
<li><a href="index.html">Docs</a> »</li>
|
|
|
|
<li>Tutorial: HTTP/2 client</li>
|
|
<li class="wy-breadcrumbs-aside">
|
|
|
|
</li>
|
|
</ul>
|
|
<hr/>
|
|
</div>
|
|
<div role="main">
|
|
|
|
<div class="section" id="tutorial-http-2-client">
|
|
<h1>Tutorial: HTTP/2 client<a class="headerlink" href="#tutorial-http-2-client" title="Permalink to this headline">¶</a></h1>
|
|
<p>In this tutorial, we are going to write very primitive HTTP/2
|
|
client. The complete source code, <a class="reference internal" href="#libevent-client-c">libevent-client.c</a>, is attached at
|
|
the end of this page. It also resides in examples directory in the
|
|
archive or repository.</p>
|
|
<p>This simple client takes 1 argument, HTTPS URI, and retrieves the
|
|
resource denoted by the URI. Its synopsis is like this:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre>$ libevent-client HTTPS_URI
|
|
</pre></div>
|
|
</div>
|
|
<p>We use libevent in this tutorial to handle networking I/O. Please
|
|
note that nghttp2 itself does not depend on libevent.</p>
|
|
<p>First we do some setup routine for libevent and OpenSSL library in
|
|
function <tt class="docutils literal"><span class="pre">main()</span></tt> and <tt class="docutils literal"><span class="pre">run()</span></tt>, which is not so relevant to nghttp2
|
|
library use. The one thing you should look at is setup NPN callback.
|
|
The NPN callback is used for the client to select the next application
|
|
protocol over the SSL/TLS transport. In this tutorial, we use
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_select_next_protocol" title="nghttp2_select_next_protocol"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_select_next_protocol()</span></tt></a> function to select the HTTP/2
|
|
protocol the library supports:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">select_next_proto_cb</span><span class="p">(</span><span class="n">SSL</span><span class="o">*</span> <span class="n">ssl</span><span class="p">,</span>
|
|
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">**</span><span class="n">out</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">outlen</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">in</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">inlen</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">nghttp2_select_next_protocol</span><span class="p">(</span><span class="n">out</span><span class="p">,</span> <span class="n">outlen</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">inlen</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Server did not advertise "</span> <span class="n">NGHTTP2_PROTO_VERSION_ID</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="n">SSL_TLSEXT_ERR_OK</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>The callback is set to the SSL_CTX object using
|
|
<tt class="docutils literal"><span class="pre">SSL_CTX_set_next_proto_select_cb()</span></tt> function:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="n">SSL_CTX</span><span class="o">*</span> <span class="nf">create_ssl_ctx</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">SSL_CTX</span> <span class="o">*</span><span class="n">ssl_ctx</span><span class="p">;</span>
|
|
<span class="n">ssl_ctx</span> <span class="o">=</span> <span class="n">SSL_CTX_new</span><span class="p">(</span><span class="n">SSLv23_client_method</span><span class="p">());</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">ssl_ctx</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not create SSL/TLS context: %s"</span><span class="p">,</span>
|
|
<span class="n">ERR_error_string</span><span class="p">(</span><span class="n">ERR_get_error</span><span class="p">(),</span> <span class="nb">NULL</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
<span class="n">SSL_CTX_set_options</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span>
|
|
<span class="n">SSL_OP_ALL</span> <span class="o">|</span> <span class="n">SSL_OP_NO_SSLv2</span> <span class="o">|</span> <span class="n">SSL_OP_NO_COMPRESSION</span> <span class="o">|</span>
|
|
<span class="n">SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION</span><span class="p">);</span>
|
|
<span class="n">SSL_CTX_set_next_proto_select_cb</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">select_next_proto_cb</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
|
|
<span class="k">return</span> <span class="n">ssl_ctx</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We use <tt class="docutils literal"><span class="pre">http2_session_data</span></tt> structure to store the data related to
|
|
the HTTP/2 session:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
|
|
<span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">evdns_base</span> <span class="o">*</span><span class="n">dnsbase</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">;</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="n">http2_session_data</span><span class="p">;</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Since this program only handles 1 URI, it uses only 1 stream. We store
|
|
its stream specific data in <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> structure and the
|
|
<tt class="docutils literal"><span class="pre">stream_data</span></tt> points to it. The <tt class="docutils literal"><span class="pre">struct</span> <span class="pre">http2_stream_data</span></tt> is
|
|
defined as follows:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
|
|
<span class="cm">/* The NULL-terminated URI string to retreive. */</span>
|
|
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">uri</span><span class="p">;</span>
|
|
<span class="cm">/* Parsed result of the |uri| */</span>
|
|
<span class="k">struct</span> <span class="n">http_parser_url</span> <span class="o">*</span><span class="n">u</span><span class="p">;</span>
|
|
<span class="cm">/* The authroity portion of the |uri|, not NULL-terminated */</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">authority</span><span class="p">;</span>
|
|
<span class="cm">/* The path portion of the |uri|, including query, not</span>
|
|
<span class="cm"> NULL-terminated */</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">;</span>
|
|
<span class="cm">/* The length of the |authority| */</span>
|
|
<span class="kt">size_t</span> <span class="n">authoritylen</span><span class="p">;</span>
|
|
<span class="cm">/* The length of the |path| */</span>
|
|
<span class="kt">size_t</span> <span class="n">pathlen</span><span class="p">;</span>
|
|
<span class="cm">/* The stream ID of this stream */</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="n">http2_stream_data</span><span class="p">;</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We creates and initializes these structures in
|
|
<tt class="docutils literal"><span class="pre">create_http2_session_data()</span></tt> and <tt class="docutils literal"><span class="pre">create_http2_stream_data()</span></tt>
|
|
respectively.</p>
|
|
<p>Then we call function <tt class="docutils literal"><span class="pre">initiate_connection()</span></tt> to start connecting to
|
|
the remote server:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">initiate_connection</span><span class="p">(</span><span class="k">struct</span> <span class="n">event_base</span> <span class="o">*</span><span class="n">evbase</span><span class="p">,</span>
|
|
<span class="n">SSL_CTX</span> <span class="o">*</span><span class="n">ssl_ctx</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">host</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">port</span><span class="p">,</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">;</span>
|
|
<span class="n">SSL</span> <span class="o">*</span><span class="n">ssl</span><span class="p">;</span>
|
|
|
|
<span class="n">ssl</span> <span class="o">=</span> <span class="n">create_ssl</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">);</span>
|
|
<span class="n">bev</span> <span class="o">=</span> <span class="n">bufferevent_openssl_socket_new</span><span class="p">(</span><span class="n">evbase</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ssl</span><span class="p">,</span>
|
|
<span class="n">BUFFEREVENT_SSL_CONNECTING</span><span class="p">,</span>
|
|
<span class="n">BEV_OPT_DEFER_CALLBACKS</span> <span class="o">|</span>
|
|
<span class="n">BEV_OPT_CLOSE_ON_FREE</span><span class="p">);</span>
|
|
<span class="n">bufferevent_setcb</span><span class="p">(</span><span class="n">bev</span><span class="p">,</span> <span class="n">readcb</span><span class="p">,</span> <span class="n">writecb</span><span class="p">,</span> <span class="n">eventcb</span><span class="p">,</span> <span class="n">session_data</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">bufferevent_socket_connect_hostname</span><span class="p">(</span><span class="n">bev</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">dnsbase</span><span class="p">,</span>
|
|
<span class="n">AF_UNSPEC</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">);</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not connect to the remote host %s"</span><span class="p">,</span> <span class="n">host</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">bev</span> <span class="o">=</span> <span class="n">bev</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We set 3 callbacks for the bufferevent: <tt class="docutils literal"><span class="pre">reacb</span></tt>, <tt class="docutils literal"><span class="pre">writecb</span></tt> and
|
|
<tt class="docutils literal"><span class="pre">eventcb</span></tt>.</p>
|
|
<p>The <tt class="docutils literal"><span class="pre">eventcb()</span></tt> is invoked by libevent event loop when an event
|
|
(e.g., connection has been established, timeout, etc) happens on the
|
|
underlying network socket:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">eventcb</span><span class="p">(</span><span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">,</span> <span class="kt">short</span> <span class="n">events</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">bufferevent_getfd</span><span class="p">(</span><span class="n">bev</span><span class="p">);</span>
|
|
<span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Connected</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="n">setsockopt</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IPPROTO_TCP</span><span class="p">,</span> <span class="n">TCP_NODELAY</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">val</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">val</span><span class="p">));</span>
|
|
<span class="n">initialize_nghttp2_session</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="n">send_client_connection_header</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="n">submit_request</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_send</span><span class="p">(</span><span class="n">session_data</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_EOF</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Disconnected from the remote host"</span><span class="p">);</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_ERROR</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Network error"</span><span class="p">);</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_TIMEOUT</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Timeout"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>For <tt class="docutils literal"><span class="pre">BEV_EVENT_EOF</span></tt>, <tt class="docutils literal"><span class="pre">BEV_EVENT_ERROR</span></tt> and <tt class="docutils literal"><span class="pre">BEV_EVENT_TIMEOUT</span></tt>
|
|
event, we just simply tear down the connection. The
|
|
<tt class="docutils literal"><span class="pre">BEV_EVENT_CONNECTED</span></tt> event is invoked when SSL/TLS handshake is
|
|
finished successfully. We first initialize nghttp2 session object in
|
|
<tt class="docutils literal"><span class="pre">initialize_nghttp2_session()</span></tt> function:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">initialize_nghttp2_session</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">nghttp2_session_callbacks</span> <span class="n">callbacks</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
|
|
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">send_callback</span> <span class="o">=</span> <span class="n">send_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">before_frame_send_callback</span> <span class="o">=</span> <span class="n">before_frame_send_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_frame_recv_callback</span> <span class="o">=</span> <span class="n">on_frame_recv_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_data_chunk_recv_callback</span> <span class="o">=</span> <span class="n">on_data_chunk_recv_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_stream_close_callback</span> <span class="o">=</span> <span class="n">on_stream_close_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_header_callback</span> <span class="o">=</span> <span class="n">on_header_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_begin_headers_callback</span> <span class="o">=</span> <span class="n">on_begin_headers_callback</span><span class="p">;</span>
|
|
<span class="n">nghttp2_session_client_new</span><span class="p">(</span><span class="o">&</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="o">&</span><span class="n">callbacks</span><span class="p">,</span> <span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Since we are creating client, we use <a class="reference internal" href="apiref.html#c.nghttp2_session_client_new" title="nghttp2_session_client_new"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_client_new()</span></tt></a> to
|
|
initialize nghttp2 session object. We setup 7 callbacks for the
|
|
nghttp2 session. We’ll explain these callbacks later.</p>
|
|
<p>The <tt class="xref c c-func docutils literal"><span class="pre">delete_http2_session_data()</span></tt> destroys <tt class="docutils literal"><span class="pre">session_data</span></tt> and frees
|
|
its bufferevent, so it closes underlying connection as well. It also
|
|
calls <a class="reference internal" href="apiref.html#c.nghttp2_session_del" title="nghttp2_session_del"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_del()</span></tt></a> to delete nghttp2 session object.</p>
|
|
<p>We begin HTTP/2 communication by sending client connection preface,
|
|
which is 24 bytes magic byte sequence
|
|
(<a class="reference internal" href="apiref.html#c.NGHTTP2_CLIENT_CONNECTION_PREFACE" title="NGHTTP2_CLIENT_CONNECTION_PREFACE"><tt class="xref c c-macro docutils literal"><span class="pre">NGHTTP2_CLIENT_CONNECTION_PREFACE</span></tt></a>) and SETTINGS frame. The
|
|
transmission of client connection header is done in
|
|
<tt class="docutils literal"><span class="pre">send_client_connection_header()</span></tt>:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">send_client_connection_header</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">nghttp2_settings_entry</span> <span class="n">iv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
|
|
<span class="p">{</span> <span class="n">NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS</span><span class="p">,</span> <span class="mi">100</span> <span class="p">}</span>
|
|
<span class="p">};</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
|
|
<span class="n">bufferevent_write</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">,</span>
|
|
<span class="n">NGHTTP2_CLIENT_CONNECTION_PREFACE</span><span class="p">,</span>
|
|
<span class="n">NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_submit_settings</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="n">NGHTTP2_FLAG_NONE</span><span class="p">,</span>
|
|
<span class="n">iv</span><span class="p">,</span> <span class="n">ARRLEN</span><span class="p">(</span><span class="n">iv</span><span class="p">));</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not submit SETTINGS: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
|
|
really not needed for this tiny example progoram, but we are
|
|
demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
|
|
for the transmission, we use <a class="reference internal" href="apiref.html#c.nghttp2_submit_settings" title="nghttp2_submit_settings"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_submit_settings()</span></tt></a>. Note that
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_submit_settings" title="nghttp2_submit_settings"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_submit_settings()</span></tt></a> function only queues the frame and not
|
|
actually send it. All <tt class="docutils literal"><span class="pre">nghttp2_submit_*()</span></tt> family functions have
|
|
this property. To actually send the frame, <a class="reference internal" href="apiref.html#c.nghttp2_session_send" title="nghttp2_session_send"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_send()</span></tt></a> is
|
|
used, which is described about later.</p>
|
|
<p>After the transmission of client connection header, we enqueue HTTP
|
|
request in <tt class="docutils literal"><span class="pre">submit_request()</span></tt> function:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">submit_request</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span> <span class="o">=</span> <span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="p">;</span>
|
|
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">uri</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">uri</span><span class="p">;</span>
|
|
<span class="k">const</span> <span class="k">struct</span> <span class="n">http_parser_url</span> <span class="o">*</span><span class="n">u</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">u</span><span class="p">;</span>
|
|
<span class="n">nghttp2_nv</span> <span class="n">hdrs</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
|
|
<span class="n">MAKE_NV2</span><span class="p">(</span><span class="s">":method"</span><span class="p">,</span> <span class="s">"GET"</span><span class="p">),</span>
|
|
<span class="n">MAKE_NV</span><span class="p">(</span><span class="s">":scheme"</span><span class="p">,</span>
|
|
<span class="o">&</span><span class="n">uri</span><span class="p">[</span><span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_SCHEMA</span><span class="p">].</span><span class="n">off</span><span class="p">],</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_SCHEMA</span><span class="p">].</span><span class="n">len</span><span class="p">),</span>
|
|
<span class="n">MAKE_NV</span><span class="p">(</span><span class="s">":authority"</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">authority</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">authoritylen</span><span class="p">),</span>
|
|
<span class="n">MAKE_NV</span><span class="p">(</span><span class="s">":path"</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">path</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">pathlen</span><span class="p">)</span>
|
|
<span class="p">};</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Request headers:</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="n">print_headers</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="n">hdrs</span><span class="p">,</span> <span class="n">ARRLEN</span><span class="p">(</span><span class="n">hdrs</span><span class="p">));</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_submit_request</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span>
|
|
<span class="n">hdrs</span><span class="p">,</span> <span class="n">ARRLEN</span><span class="p">(</span><span class="n">hdrs</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not submit HTTP request: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We build HTTP request header fields in <tt class="docutils literal"><span class="pre">hdrs</span></tt> which is an array of
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_nv" title="nghttp2_nv"><tt class="xref c c-type docutils literal"><span class="pre">nghttp2_nv</span></tt></a>. There are 4 header fields to be sent: <tt class="docutils literal"><span class="pre">:method</span></tt>,
|
|
<tt class="docutils literal"><span class="pre">:scheme</span></tt>, <tt class="docutils literal"><span class="pre">:authority</span></tt> and <tt class="docutils literal"><span class="pre">:path</span></tt>. To queue this HTTP request,
|
|
we use <a class="reference internal" href="apiref.html#c.nghttp2_submit_request" title="nghttp2_submit_request"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_submit_request()</span></tt></a> function. The <tt class="xref c c-func docutils literal"><span class="pre">stream_data()</span></tt> is
|
|
passed in <em>stream_user_data</em> parameter. It is used in nghttp2
|
|
callbacks which we’ll describe about later.</p>
|
|
<p>The next bufferevent callback is <tt class="docutils literal"><span class="pre">readcb()</span></tt>, which is invoked when
|
|
data is available to read in the bufferevent input buffer:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">readcb</span><span class="p">(</span><span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">evbuffer</span> <span class="o">*</span><span class="n">input</span> <span class="o">=</span> <span class="n">bufferevent_get_input</span><span class="p">(</span><span class="n">bev</span><span class="p">);</span>
|
|
<span class="kt">size_t</span> <span class="n">datalen</span> <span class="o">=</span> <span class="n">evbuffer_get_length</span><span class="p">(</span><span class="n">input</span><span class="p">);</span>
|
|
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">evbuffer_pullup</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_session_mem_recv</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">datalen</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Fatal error: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="k">return</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="n">evbuffer_drain</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">rv</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_send</span><span class="p">(</span><span class="n">session_data</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="k">return</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>In this function, we feed all unprocessed, received data to nghttp2
|
|
session object using <a class="reference internal" href="apiref.html#c.nghttp2_session_mem_recv" title="nghttp2_session_mem_recv"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_mem_recv()</span></tt></a> function. The
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_session_mem_recv" title="nghttp2_session_mem_recv"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_mem_recv()</span></tt></a> processes the received data and may
|
|
invoke nghttp2 callbacks and also queue frames. Since there may be
|
|
pending frames, we call <tt class="docutils literal"><span class="pre">session_send()</span></tt> function to send those
|
|
frames. The <tt class="docutils literal"><span class="pre">session_send()</span></tt> function is defined as follows:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">session_send</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_session_send</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Fatal error: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>The <a class="reference internal" href="apiref.html#c.nghttp2_session_send" title="nghttp2_session_send"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_send()</span></tt></a> function serializes the frame into wire
|
|
format and call <a class="reference internal" href="apiref.html#c.nghttp2_session_callbacks.send_callback" title="nghttp2_session_callbacks.send_callback"><tt class="xref c c-member docutils literal"><span class="pre">nghttp2_session_callbacks.send_callback</span></tt></a> with
|
|
it. We set <tt class="docutils literal"><span class="pre">send_callback()</span></tt> function to
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_session_callbacks.send_callback" title="nghttp2_session_callbacks.send_callback"><tt class="xref c c-member docutils literal"><span class="pre">nghttp2_session_callbacks.send_callback</span></tt></a> in
|
|
<tt class="docutils literal"><span class="pre">initialize_nghttp2_session()</span></tt> function described earlier. It is
|
|
defined as follows:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">ssize_t</span> <span class="nf">send_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span>
|
|
<span class="kt">int</span> <span class="n">flags</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span> <span class="o">=</span> <span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">;</span>
|
|
<span class="n">bufferevent_write</span><span class="p">(</span><span class="n">bev</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>
|
|
<span class="k">return</span> <span class="n">length</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Since we use bufferevent to abstract network I/O, we just write the
|
|
data to the bufferevent object. Note that <a class="reference internal" href="apiref.html#c.nghttp2_session_send" title="nghttp2_session_send"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_send()</span></tt></a>
|
|
continues to write all frames queued so far. If we were writing the
|
|
data to the non-blocking socket directly using <tt class="docutils literal"><span class="pre">write()</span></tt> system call
|
|
in the <a class="reference internal" href="apiref.html#c.nghttp2_session_callbacks.send_callback" title="nghttp2_session_callbacks.send_callback"><tt class="xref c c-member docutils literal"><span class="pre">nghttp2_session_callbacks.send_callback</span></tt></a>, we will
|
|
surely get <tt class="docutils literal"><span class="pre">EAGAIN</span></tt> or <tt class="docutils literal"><span class="pre">EWOULDBLOCK</span></tt> since the socket has limited
|
|
send buffer. If that happens, we can return
|
|
<a class="reference internal" href="apiref.html#c.NGHTTP2_ERR_WOULDBLOCK" title="NGHTTP2_ERR_WOULDBLOCK"><tt class="xref c c-macro docutils literal"><span class="pre">NGHTTP2_ERR_WOULDBLOCK</span></tt></a> to signal the nghttp2 library to stop
|
|
sending further data. But writing to the bufferevent, we have to
|
|
regulate the amount data to be buffered by ourselves to avoid possible
|
|
huge memory consumption. In this example client, we do not limit
|
|
anything. To see how to regulate the amount of buffered data, see the
|
|
<tt class="docutils literal"><span class="pre">send_callback()</span></tt> in the server tutorial.</p>
|
|
<p>The third bufferevent callback is <tt class="docutils literal"><span class="pre">writecb()</span></tt>, which is invoked when
|
|
all data written in the bufferevent output buffer have been sent:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">writecb</span><span class="p">(</span><span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">nghttp2_session_want_read</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&&</span>
|
|
<span class="n">nghttp2_session_want_write</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&&</span>
|
|
<span class="n">evbuffer_get_length</span><span class="p">(</span><span class="n">bufferevent_get_output</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>As described earlier, we just write off all data in <tt class="xref c c-func docutils literal"><span class="pre">send_callback()</span></tt>,
|
|
we have no data to write in this function. All we have to do is check
|
|
we have to drop connection or not. The nghttp2 session object keeps
|
|
track of reception and transmission of GOAWAY frame and other error
|
|
conditions as well. Using these information, nghttp2 session object
|
|
will tell whether the connection should be dropped or not. More
|
|
specifically, both <a class="reference internal" href="apiref.html#c.nghttp2_session_want_read" title="nghttp2_session_want_read"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_want_read()</span></tt></a> and
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_session_want_write" title="nghttp2_session_want_write"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_want_write()</span></tt></a> return 0, we have no business in the
|
|
connection. But since we are using bufferevent and its deferred
|
|
callback option, the bufferevent output buffer may contain the pending
|
|
data when the <tt class="docutils literal"><span class="pre">writecb()</span></tt> is called. To handle this situation, we
|
|
also check whether the output buffer is empty or not. If these
|
|
conditions are met, we drop connection.</p>
|
|
<p>We have already described about nghttp2 callback <tt class="docutils literal"><span class="pre">send_callback()</span></tt>.
|
|
Let’s describe remaining nghttp2 callbacks we setup in
|
|
<tt class="docutils literal"><span class="pre">initialize_nghttp2_setup()</span></tt> function.</p>
|
|
<p>The <tt class="xref c c-func docutils literal"><span class="pre">before_frame_send_callback()</span></tt> function is invoked when a frame is
|
|
about to be sent:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">before_frame_send_callback</span>
|
|
<span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span> <span class="k">const</span> <span class="n">nghttp2_frame</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span><span class="p">;</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">NGHTTP2_HEADERS</span> <span class="o">&&</span>
|
|
<span class="n">frame</span><span class="o">-></span><span class="n">headers</span><span class="p">.</span><span class="n">cat</span> <span class="o">==</span> <span class="n">NGHTTP2_HCAT_REQUEST</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span> <span class="o">=</span>
|
|
<span class="p">(</span><span class="n">http2_stream_data</span><span class="o">*</span><span class="p">)</span><span class="n">nghttp2_session_get_stream_user_data</span>
|
|
<span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">stream_data</span> <span class="o">==</span> <span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">=</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Remember that we have not get stream ID when we submit HTTP request
|
|
using <a class="reference internal" href="apiref.html#c.nghttp2_submit_request" title="nghttp2_submit_request"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_submit_request()</span></tt></a>. Since nghttp2 library reorders the
|
|
request based on priority and stream ID must be monotonically
|
|
increased, the stream ID is not assigned just before transmission.
|
|
The one of the purpose of this callback is get the stream ID assigned
|
|
to the frame. First we check that the frame is HEADERS frame. Since
|
|
HEADERS has several meanings in HTTP/2, we check that it is request
|
|
HEADERS (which means that the first HEADERS frame to create a stream).
|
|
The assigned stream ID is <tt class="docutils literal"><span class="pre">frame->hd.stream_id</span></tt>. Recall that we
|
|
passed <tt class="docutils literal"><span class="pre">stream_data</span></tt> in the <em>stream_user_data</em> parameter of
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_submit_request" title="nghttp2_submit_request"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_submit_request()</span></tt></a> function. We can get it using
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_session_get_stream_user_data" title="nghttp2_session_get_stream_user_data"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_get_stream_user_data()</span></tt></a> function. To really sure that
|
|
this HEADERS frame is the request HEADERS we have queued, we check
|
|
that <tt class="docutils literal"><span class="pre">session_data->stream_data</span></tt> and <tt class="docutils literal"><span class="pre">stream_data</span></tt> returned from
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_session_get_stream_user_data" title="nghttp2_session_get_stream_user_data"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_get_stream_user_data()</span></tt></a> are pointing the same
|
|
location. In this example program, we just only uses 1 stream, it is
|
|
unnecessary to compare them, but real applications surely deal with
|
|
multiple streams, and <em>stream_user_data</em> is very handy to identify
|
|
which HEADERS we are seeing in the callback. Therefore we just show
|
|
how to use it here.</p>
|
|
<p>Each request header name/value pair is emitted via
|
|
<tt class="docutils literal"><span class="pre">on_header_callback</span></tt> function:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">on_header_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="n">nghttp2_frame</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">namelen</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">value</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">valuelen</span><span class="p">,</span>
|
|
<span class="kt">uint8_t</span> <span class="n">flags</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">switch</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">case</span> <span class="n">NGHTTP2_HEADERS</span>:
|
|
<span class="k">if</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">headers</span><span class="p">.</span><span class="n">cat</span> <span class="o">==</span> <span class="n">NGHTTP2_HCAT_RESPONSE</span> <span class="o">&&</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="cm">/* Print response headers for the initiated request. */</span>
|
|
<span class="n">print_header</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">namelen</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">valuelen</span><span class="p">);</span>
|
|
<span class="k">break</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>In this turotial, we just print the name/value pair.</p>
|
|
<p>After all name/value pairs are emitted for a frame,
|
|
<tt class="docutils literal"><span class="pre">on_frame_recv_callback</span></tt> function is called:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">on_frame_recv_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="n">nghttp2_frame</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">switch</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">case</span> <span class="n">NGHTTP2_HEADERS</span>:
|
|
<span class="k">if</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">headers</span><span class="p">.</span><span class="n">cat</span> <span class="o">==</span> <span class="n">NGHTTP2_HCAT_RESPONSE</span> <span class="o">&&</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"All headers received</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">break</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>In this tutorial, we are just interested in the HTTP response
|
|
HEADERS. We check te frame type and its category (it should be
|
|
<a class="reference internal" href="apiref.html#c.NGHTTP2_HCAT_RESPONSE" title="NGHTTP2_HCAT_RESPONSE"><tt class="xref c c-macro docutils literal"><span class="pre">NGHTTP2_HCAT_RESPONSE</span></tt></a> for HTTP response HEADERS). Also check
|
|
its stream ID.</p>
|
|
<p>The <tt class="docutils literal"><span class="pre">on_data_chunk_recv_callback()</span></tt> function is invoked when a chunk
|
|
of data is received from the remote peer:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">on_data_chunk_recv_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">flags</span><span class="p">,</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fwrite</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">stdout</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>In our case, a chunk of data is response body. After checking stream
|
|
ID, we just write the recieved data to the stdout. Note that the
|
|
output in the terminal may be corrupted if the response body contains
|
|
some binary data.</p>
|
|
<p>The <tt class="docutils literal"><span class="pre">on_stream_close_callback()</span></tt> function is invoked when the stream
|
|
is about to close:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">on_stream_close_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">,</span>
|
|
<span class="n">nghttp2_error_code</span> <span class="n">error_code</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Stream %d closed with error_code=%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
|
|
<span class="n">stream_id</span><span class="p">,</span> <span class="n">error_code</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_session_terminate_session</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">NGHTTP2_NO_ERROR</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">NGHTTP2_ERR_CALLBACK_FAILURE</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>If the stream ID matches the one we initiated, it means that its
|
|
stream is going to be closed. Since we have finished to get the
|
|
resource we want (or the stream was reset by RST_STREAM from the
|
|
remote peer), we call <a class="reference internal" href="apiref.html#c.nghttp2_session_terminate_session" title="nghttp2_session_terminate_session"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_terminate_session()</span></tt></a> to
|
|
commencing the closure of the HTTP/2 session gracefully. If you have
|
|
some data associated for the stream to be closed, you may delete it
|
|
here.</p>
|
|
<div class="section" id="libevent-client-c">
|
|
<h2>libevent-client.c<a class="headerlink" href="#libevent-client-c" title="Permalink to this headline">¶</a></h2>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="cm">/*</span>
|
|
<span class="cm"> * nghttp2 - HTTP/2 C Library</span>
|
|
<span class="cm"> *</span>
|
|
<span class="cm"> * Copyright (c) 2013 Tatsuhiro Tsujikawa</span>
|
|
<span class="cm"> *</span>
|
|
<span class="cm"> * Permission is hereby granted, free of charge, to any person obtaining</span>
|
|
<span class="cm"> * a copy of this software and associated documentation files (the</span>
|
|
<span class="cm"> * "Software"), to deal in the Software without restriction, including</span>
|
|
<span class="cm"> * without limitation the rights to use, copy, modify, merge, publish,</span>
|
|
<span class="cm"> * distribute, sublicense, and/or sell copies of the Software, and to</span>
|
|
<span class="cm"> * permit persons to whom the Software is furnished to do so, subject to</span>
|
|
<span class="cm"> * the following conditions:</span>
|
|
<span class="cm"> *</span>
|
|
<span class="cm"> * The above copyright notice and this permission notice shall be</span>
|
|
<span class="cm"> * included in all copies or substantial portions of the Software.</span>
|
|
<span class="cm"> *</span>
|
|
<span class="cm"> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,</span>
|
|
<span class="cm"> * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF</span>
|
|
<span class="cm"> * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND</span>
|
|
<span class="cm"> * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE</span>
|
|
<span class="cm"> * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION</span>
|
|
<span class="cm"> * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION</span>
|
|
<span class="cm"> * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</span>
|
|
<span class="cm"> */</span>
|
|
<span class="cp">#include <sys/types.h></span>
|
|
<span class="cp">#include <unistd.h></span>
|
|
<span class="cp">#include <sys/socket.h></span>
|
|
<span class="cp">#include <netinet/in.h></span>
|
|
<span class="cp">#include <netinet/tcp.h></span>
|
|
<span class="cp">#include <err.h></span>
|
|
<span class="cp">#include <signal.h></span>
|
|
|
|
<span class="cp">#include <openssl/ssl.h></span>
|
|
<span class="cp">#include <openssl/err.h></span>
|
|
|
|
<span class="cp">#include <event.h></span>
|
|
<span class="cp">#include <event2/event.h></span>
|
|
<span class="cp">#include <event2/bufferevent_ssl.h></span>
|
|
<span class="cp">#include <event2/dns.h></span>
|
|
|
|
<span class="cp">#include <nghttp2/nghttp2.h></span>
|
|
|
|
<span class="cp">#include "http-parser/http_parser.h"</span>
|
|
|
|
<span class="cp">#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))</span>
|
|
|
|
<span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
|
|
<span class="cm">/* The NULL-terminated URI string to retreive. */</span>
|
|
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">uri</span><span class="p">;</span>
|
|
<span class="cm">/* Parsed result of the |uri| */</span>
|
|
<span class="k">struct</span> <span class="n">http_parser_url</span> <span class="o">*</span><span class="n">u</span><span class="p">;</span>
|
|
<span class="cm">/* The authroity portion of the |uri|, not NULL-terminated */</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">authority</span><span class="p">;</span>
|
|
<span class="cm">/* The path portion of the |uri|, including query, not</span>
|
|
<span class="cm"> NULL-terminated */</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">;</span>
|
|
<span class="cm">/* The length of the |authority| */</span>
|
|
<span class="kt">size_t</span> <span class="n">authoritylen</span><span class="p">;</span>
|
|
<span class="cm">/* The length of the |path| */</span>
|
|
<span class="kt">size_t</span> <span class="n">pathlen</span><span class="p">;</span>
|
|
<span class="cm">/* The stream ID of this stream */</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="n">http2_stream_data</span><span class="p">;</span>
|
|
|
|
<span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
|
|
<span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">evdns_base</span> <span class="o">*</span><span class="n">dnsbase</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">;</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="n">http2_session_data</span><span class="p">;</span>
|
|
|
|
<span class="k">static</span> <span class="n">http2_stream_data</span><span class="o">*</span> <span class="nf">create_http2_stream_data</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">uri</span><span class="p">,</span>
|
|
<span class="k">struct</span> <span class="n">http_parser_url</span> <span class="o">*</span><span class="n">u</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="cm">/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */</span>
|
|
<span class="kt">size_t</span> <span class="n">extra</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">http2_stream_data</span><span class="p">));</span>
|
|
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">uri</span> <span class="o">=</span> <span class="n">uri</span><span class="p">;</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">u</span> <span class="o">=</span> <span class="n">u</span><span class="p">;</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
|
|
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">authoritylen</span> <span class="o">=</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_HOST</span><span class="p">].</span><span class="n">len</span><span class="p">;</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">authority</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">authoritylen</span> <span class="o">+</span> <span class="n">extra</span><span class="p">);</span>
|
|
<span class="n">memcpy</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">authority</span><span class="p">,</span>
|
|
<span class="o">&</span><span class="n">uri</span><span class="p">[</span><span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_HOST</span><span class="p">].</span><span class="n">off</span><span class="p">],</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_HOST</span><span class="p">].</span><span class="n">len</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">u</span><span class="o">-></span><span class="n">field_set</span> <span class="o">&</span> <span class="p">(</span><span class="mi">1</span> <span class="o"><<</span> <span class="n">UF_PORT</span><span class="p">))</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">authoritylen</span> <span class="o">+=</span>
|
|
<span class="n">snprintf</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">authority</span> <span class="o">+</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_HOST</span><span class="p">].</span><span class="n">len</span><span class="p">,</span> <span class="n">extra</span><span class="p">,</span>
|
|
<span class="s">":%u"</span><span class="p">,</span> <span class="n">u</span><span class="o">-></span><span class="n">port</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">pathlen</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">u</span><span class="o">-></span><span class="n">field_set</span> <span class="o">&</span> <span class="p">(</span><span class="mi">1</span> <span class="o"><<</span> <span class="n">UF_PATH</span><span class="p">))</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">pathlen</span> <span class="o">=</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_PATH</span><span class="p">].</span><span class="n">len</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">u</span><span class="o">-></span><span class="n">field_set</span> <span class="o">&</span> <span class="p">(</span><span class="mi">1</span> <span class="o"><<</span> <span class="n">UF_QUERY</span><span class="p">))</span> <span class="p">{</span>
|
|
<span class="cm">/* +1 for '?' character */</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">pathlen</span> <span class="o">+=</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_QUERY</span><span class="p">].</span><span class="n">len</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">pathlen</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">path</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">pathlen</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">u</span><span class="o">-></span><span class="n">field_set</span> <span class="o">&</span> <span class="p">(</span><span class="mi">1</span> <span class="o"><<</span> <span class="n">UF_PATH</span><span class="p">))</span> <span class="p">{</span>
|
|
<span class="n">memcpy</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">path</span><span class="p">,</span>
|
|
<span class="o">&</span><span class="n">uri</span><span class="p">[</span><span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_PATH</span><span class="p">].</span><span class="n">off</span><span class="p">],</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_PATH</span><span class="p">].</span><span class="n">len</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">u</span><span class="o">-></span><span class="n">field_set</span> <span class="o">&</span> <span class="p">(</span><span class="mi">1</span> <span class="o"><<</span> <span class="n">UF_QUERY</span><span class="p">))</span> <span class="p">{</span>
|
|
<span class="n">memcpy</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">path</span> <span class="o">+</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_PATH</span><span class="p">].</span><span class="n">len</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span>
|
|
<span class="o">&</span><span class="n">uri</span><span class="p">[</span><span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_QUERY</span><span class="p">].</span><span class="n">off</span><span class="p">],</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_QUERY</span><span class="p">].</span><span class="n">len</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">path</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="n">stream_data</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">delete_http2_stream_data</span><span class="p">(</span><span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">free</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">path</span><span class="p">);</span>
|
|
<span class="n">free</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">authority</span><span class="p">);</span>
|
|
<span class="n">free</span><span class="p">(</span><span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Initializes |session_data| */</span>
|
|
<span class="k">static</span> <span class="n">http2_session_data</span> <span class="o">*</span><span class="nf">create_http2_session_data</span><span class="p">(</span><span class="k">struct</span> <span class="n">event_base</span> <span class="o">*</span><span class="n">evbase</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">http2_session_data</span><span class="p">));</span>
|
|
|
|
<span class="n">memset</span><span class="p">(</span><span class="n">session_data</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">http2_session_data</span><span class="p">));</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">dnsbase</span> <span class="o">=</span> <span class="n">evdns_base_new</span><span class="p">(</span><span class="n">evbase</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
|
|
<span class="k">return</span> <span class="n">session_data</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">delete_http2_session_data</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">SSL</span> <span class="o">*</span><span class="n">ssl</span> <span class="o">=</span> <span class="n">bufferevent_openssl_get_ssl</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">);</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">ssl</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">SSL_shutdown</span><span class="p">(</span><span class="n">ssl</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="n">bufferevent_free</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">);</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">bev</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
|
<span class="n">evdns_base_free</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">dnsbase</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">dnsbase</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
|
<span class="n">nghttp2_session_del</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">);</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">session</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">delete_http2_stream_data</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="n">free</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">print_header</span><span class="p">(</span><span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">namelen</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">value</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">valuelen</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">fwrite</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">namelen</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">": "</span><span class="p">);</span>
|
|
<span class="n">fwrite</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">valuelen</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Print HTTP headers to |f|. Please note that this function does not</span>
|
|
<span class="cm"> take into account that header name and value are sequence of</span>
|
|
<span class="cm"> octets, therefore they may contain non-printable characters. */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">print_headers</span><span class="p">(</span><span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span><span class="p">,</span> <span class="n">nghttp2_nv</span> <span class="o">*</span><span class="n">nva</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">nvlen</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">size_t</span> <span class="n">i</span><span class="p">;</span>
|
|
<span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">nvlen</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">print_header</span><span class="p">(</span><span class="n">f</span><span class="p">,</span>
|
|
<span class="n">nva</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">name</span><span class="p">,</span> <span class="n">nva</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">namelen</span><span class="p">,</span>
|
|
<span class="n">nva</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">value</span><span class="p">,</span> <span class="n">nva</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">valuelen</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,</span>
|
|
<span class="cm"> to the network. Because we are using libevent bufferevent, we just</span>
|
|
<span class="cm"> write those bytes into bufferevent buffer. */</span>
|
|
<span class="k">static</span> <span class="kt">ssize_t</span> <span class="nf">send_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span>
|
|
<span class="kt">int</span> <span class="n">flags</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span> <span class="o">=</span> <span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">;</span>
|
|
<span class="n">bufferevent_write</span><span class="p">(</span><span class="n">bev</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>
|
|
<span class="k">return</span> <span class="n">length</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* nghttp2_before_frame_send_callback: Called when nghttp2 library is</span>
|
|
<span class="cm"> about to send a frame. We use this callback to get stream ID of new</span>
|
|
<span class="cm"> stream. Since HEADERS in HTTP/2 has several roles, we check that</span>
|
|
<span class="cm"> it is a HTTP request HEADERS. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">before_frame_send_callback</span>
|
|
<span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span> <span class="k">const</span> <span class="n">nghttp2_frame</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span><span class="p">;</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">NGHTTP2_HEADERS</span> <span class="o">&&</span>
|
|
<span class="n">frame</span><span class="o">-></span><span class="n">headers</span><span class="p">.</span><span class="n">cat</span> <span class="o">==</span> <span class="n">NGHTTP2_HCAT_REQUEST</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span> <span class="o">=</span>
|
|
<span class="p">(</span><span class="n">http2_stream_data</span><span class="o">*</span><span class="p">)</span><span class="n">nghttp2_session_get_stream_user_data</span>
|
|
<span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">stream_data</span> <span class="o">==</span> <span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">=</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* nghttp2_on_header_callback: Called when nghttp2 library emits</span>
|
|
<span class="cm"> single header name/value pair. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">on_header_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="n">nghttp2_frame</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">namelen</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">value</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">valuelen</span><span class="p">,</span>
|
|
<span class="kt">uint8_t</span> <span class="n">flags</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">switch</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">case</span> <span class="n">NGHTTP2_HEADERS</span>:
|
|
<span class="k">if</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">headers</span><span class="p">.</span><span class="n">cat</span> <span class="o">==</span> <span class="n">NGHTTP2_HCAT_RESPONSE</span> <span class="o">&&</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="cm">/* Print response headers for the initiated request. */</span>
|
|
<span class="n">print_header</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">namelen</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">valuelen</span><span class="p">);</span>
|
|
<span class="k">break</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets</span>
|
|
<span class="cm"> started to receive header block. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">on_begin_headers_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="n">nghttp2_frame</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">switch</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">case</span> <span class="n">NGHTTP2_HEADERS</span>:
|
|
<span class="k">if</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">headers</span><span class="p">.</span><span class="n">cat</span> <span class="o">==</span> <span class="n">NGHTTP2_HCAT_RESPONSE</span> <span class="o">&&</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Response headers for stream ID=%d:</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
|
|
<span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">break</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* nghttp2_on_frame_recv_callback: Called when nghttp2 library</span>
|
|
<span class="cm"> received a complete frame from the remote peer. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">on_frame_recv_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="n">nghttp2_frame</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">switch</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">case</span> <span class="n">NGHTTP2_HEADERS</span>:
|
|
<span class="k">if</span><span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">headers</span><span class="p">.</span><span class="n">cat</span> <span class="o">==</span> <span class="n">NGHTTP2_HCAT_RESPONSE</span> <span class="o">&&</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"All headers received</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">break</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is</span>
|
|
<span class="cm"> received from the remote peer. In this implementation, if the frame</span>
|
|
<span class="cm"> is meant to the stream we initiated, print the received data in</span>
|
|
<span class="cm"> stdout, so that the user can redirect its output to the file</span>
|
|
<span class="cm"> easily. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">on_data_chunk_recv_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">flags</span><span class="p">,</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fwrite</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">stdout</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* nghttp2_on_stream_close_callback: Called when a stream is about to</span>
|
|
<span class="cm"> closed. This example program only deals with 1 HTTP request (1</span>
|
|
<span class="cm"> stream), if it is closed, we send GOAWAY and tear down the</span>
|
|
<span class="cm"> session */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">on_stream_close_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span><span class="p">,</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">,</span>
|
|
<span class="n">nghttp2_error_code</span> <span class="n">error_code</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span> <span class="o">==</span> <span class="n">stream_id</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Stream %d closed with error_code=%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
|
|
<span class="n">stream_id</span><span class="p">,</span> <span class="n">error_code</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_session_terminate_session</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">NGHTTP2_NO_ERROR</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">NGHTTP2_ERR_CALLBACK_FAILURE</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* NPN TLS extension client callback. We check that server advertised</span>
|
|
<span class="cm"> the HTTP/2 protocol the nghttp2 library supports. If not, exit</span>
|
|
<span class="cm"> the program. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">select_next_proto_cb</span><span class="p">(</span><span class="n">SSL</span><span class="o">*</span> <span class="n">ssl</span><span class="p">,</span>
|
|
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">**</span><span class="n">out</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">outlen</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">in</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">inlen</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">nghttp2_select_next_protocol</span><span class="p">(</span><span class="n">out</span><span class="p">,</span> <span class="n">outlen</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">inlen</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Server did not advertise "</span> <span class="n">NGHTTP2_PROTO_VERSION_ID</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="n">SSL_TLSEXT_ERR_OK</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Create SSL_CTX. */</span>
|
|
<span class="k">static</span> <span class="n">SSL_CTX</span><span class="o">*</span> <span class="nf">create_ssl_ctx</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">SSL_CTX</span> <span class="o">*</span><span class="n">ssl_ctx</span><span class="p">;</span>
|
|
<span class="n">ssl_ctx</span> <span class="o">=</span> <span class="n">SSL_CTX_new</span><span class="p">(</span><span class="n">SSLv23_client_method</span><span class="p">());</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">ssl_ctx</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not create SSL/TLS context: %s"</span><span class="p">,</span>
|
|
<span class="n">ERR_error_string</span><span class="p">(</span><span class="n">ERR_get_error</span><span class="p">(),</span> <span class="nb">NULL</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
<span class="n">SSL_CTX_set_options</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span>
|
|
<span class="n">SSL_OP_ALL</span> <span class="o">|</span> <span class="n">SSL_OP_NO_SSLv2</span> <span class="o">|</span> <span class="n">SSL_OP_NO_COMPRESSION</span> <span class="o">|</span>
|
|
<span class="n">SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION</span><span class="p">);</span>
|
|
<span class="n">SSL_CTX_set_next_proto_select_cb</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">select_next_proto_cb</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
|
|
<span class="k">return</span> <span class="n">ssl_ctx</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Create SSL object */</span>
|
|
<span class="k">static</span> <span class="n">SSL</span><span class="o">*</span> <span class="nf">create_ssl</span><span class="p">(</span><span class="n">SSL_CTX</span> <span class="o">*</span><span class="n">ssl_ctx</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">SSL</span> <span class="o">*</span><span class="n">ssl</span><span class="p">;</span>
|
|
<span class="n">ssl</span> <span class="o">=</span> <span class="n">SSL_new</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">ssl</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not create SSL/TLS session object: %s"</span><span class="p">,</span>
|
|
<span class="n">ERR_error_string</span><span class="p">(</span><span class="n">ERR_get_error</span><span class="p">(),</span> <span class="nb">NULL</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="n">ssl</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">initialize_nghttp2_session</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">nghttp2_session_callbacks</span> <span class="n">callbacks</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
|
|
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">send_callback</span> <span class="o">=</span> <span class="n">send_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">before_frame_send_callback</span> <span class="o">=</span> <span class="n">before_frame_send_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_frame_recv_callback</span> <span class="o">=</span> <span class="n">on_frame_recv_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_data_chunk_recv_callback</span> <span class="o">=</span> <span class="n">on_data_chunk_recv_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_stream_close_callback</span> <span class="o">=</span> <span class="n">on_stream_close_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_header_callback</span> <span class="o">=</span> <span class="n">on_header_callback</span><span class="p">;</span>
|
|
<span class="n">callbacks</span><span class="p">.</span><span class="n">on_begin_headers_callback</span> <span class="o">=</span> <span class="n">on_begin_headers_callback</span><span class="p">;</span>
|
|
<span class="n">nghttp2_session_client_new</span><span class="p">(</span><span class="o">&</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="o">&</span><span class="n">callbacks</span><span class="p">,</span> <span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">send_client_connection_header</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">nghttp2_settings_entry</span> <span class="n">iv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
|
|
<span class="p">{</span> <span class="n">NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS</span><span class="p">,</span> <span class="mi">100</span> <span class="p">}</span>
|
|
<span class="p">};</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
|
|
<span class="n">bufferevent_write</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">,</span>
|
|
<span class="n">NGHTTP2_CLIENT_CONNECTION_PREFACE</span><span class="p">,</span>
|
|
<span class="n">NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_submit_settings</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="n">NGHTTP2_FLAG_NONE</span><span class="p">,</span>
|
|
<span class="n">iv</span><span class="p">,</span> <span class="n">ARRLEN</span><span class="p">(</span><span class="n">iv</span><span class="p">));</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not submit SETTINGS: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cp">#define MAKE_NV(NAME, VALUE, VALUELEN) \</span>
|
|
<span class="cp"> { (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN, \</span>
|
|
<span class="cp"> NGHTTP2_NV_FLAG_NONE }</span>
|
|
|
|
<span class="cp">#define MAKE_NV2(NAME, VALUE) \</span>
|
|
<span class="cp"> { (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \</span>
|
|
<span class="cp"> NGHTTP2_NV_FLAG_NONE }</span>
|
|
|
|
<span class="cm">/* Send HTTP request to the remote peer */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">submit_request</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</span> <span class="o">=</span> <span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span><span class="p">;</span>
|
|
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">uri</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">uri</span><span class="p">;</span>
|
|
<span class="k">const</span> <span class="k">struct</span> <span class="n">http_parser_url</span> <span class="o">*</span><span class="n">u</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">u</span><span class="p">;</span>
|
|
<span class="n">nghttp2_nv</span> <span class="n">hdrs</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
|
|
<span class="n">MAKE_NV2</span><span class="p">(</span><span class="s">":method"</span><span class="p">,</span> <span class="s">"GET"</span><span class="p">),</span>
|
|
<span class="n">MAKE_NV</span><span class="p">(</span><span class="s">":scheme"</span><span class="p">,</span>
|
|
<span class="o">&</span><span class="n">uri</span><span class="p">[</span><span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_SCHEMA</span><span class="p">].</span><span class="n">off</span><span class="p">],</span> <span class="n">u</span><span class="o">-></span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_SCHEMA</span><span class="p">].</span><span class="n">len</span><span class="p">),</span>
|
|
<span class="n">MAKE_NV</span><span class="p">(</span><span class="s">":authority"</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">authority</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">authoritylen</span><span class="p">),</span>
|
|
<span class="n">MAKE_NV</span><span class="p">(</span><span class="s">":path"</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">path</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">pathlen</span><span class="p">)</span>
|
|
<span class="p">};</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Request headers:</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="n">print_headers</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="n">hdrs</span><span class="p">,</span> <span class="n">ARRLEN</span><span class="p">(</span><span class="n">hdrs</span><span class="p">));</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_submit_request</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span>
|
|
<span class="n">hdrs</span><span class="p">,</span> <span class="n">ARRLEN</span><span class="p">(</span><span class="n">hdrs</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not submit HTTP request: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Serialize the frame and send (or buffer) the data to</span>
|
|
<span class="cm"> bufferevent. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">session_send</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_session_send</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Fatal error: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* readcb for bufferevent. Here we get the data from the input buffer</span>
|
|
<span class="cm"> of bufferevent and feed them to nghttp2 library. This may invoke</span>
|
|
<span class="cm"> nghttp2 callbacks. It may also queues the frame in nghttp2 session</span>
|
|
<span class="cm"> context. To send them, we call session_send() in the end. */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">readcb</span><span class="p">(</span><span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">evbuffer</span> <span class="o">*</span><span class="n">input</span> <span class="o">=</span> <span class="n">bufferevent_get_input</span><span class="p">(</span><span class="n">bev</span><span class="p">);</span>
|
|
<span class="kt">size_t</span> <span class="n">datalen</span> <span class="o">=</span> <span class="n">evbuffer_get_length</span><span class="p">(</span><span class="n">input</span><span class="p">);</span>
|
|
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">evbuffer_pullup</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_session_mem_recv</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">datalen</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Fatal error: %s"</span><span class="p">,</span> <span class="n">nghttp2_strerror</span><span class="p">(</span><span class="n">rv</span><span class="p">));</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="k">return</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="n">evbuffer_drain</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">rv</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_send</span><span class="p">(</span><span class="n">session_data</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="k">return</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* writecb for bufferevent. To greaceful shutdown after sending or</span>
|
|
<span class="cm"> receiving GOAWAY, we check the some conditions on the nghttp2</span>
|
|
<span class="cm"> library and output buffer of bufferevent. If it indicates we have</span>
|
|
<span class="cm"> no business to this session, tear down the connection. */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">writecb</span><span class="p">(</span><span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">nghttp2_session_want_read</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&&</span>
|
|
<span class="n">nghttp2_session_want_write</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&&</span>
|
|
<span class="n">evbuffer_get_length</span><span class="p">(</span><span class="n">bufferevent_get_output</span><span class="p">(</span><span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* eventcb for bufferevent. For the purpose of simplicity and</span>
|
|
<span class="cm"> readability of the example program, we omitted the certificate and</span>
|
|
<span class="cm"> peer verification. After SSL/TLS handshake is over, initialize</span>
|
|
<span class="cm"> nghttp2 library session, and send client connection header. Then</span>
|
|
<span class="cm"> send HTTP request. */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">eventcb</span><span class="p">(</span><span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">,</span> <span class="kt">short</span> <span class="n">events</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_CONNECTED</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">bufferevent_getfd</span><span class="p">(</span><span class="n">bev</span><span class="p">);</span>
|
|
<span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Connected</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="n">setsockopt</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IPPROTO_TCP</span><span class="p">,</span> <span class="n">TCP_NODELAY</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">val</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">val</span><span class="p">));</span>
|
|
<span class="n">initialize_nghttp2_session</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="n">send_client_connection_header</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="n">submit_request</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">session_send</span><span class="p">(</span><span class="n">session_data</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_EOF</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Disconnected from the remote host"</span><span class="p">);</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_ERROR</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Network error"</span><span class="p">);</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">events</span> <span class="o">&</span> <span class="n">BEV_EVENT_TIMEOUT</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">warnx</span><span class="p">(</span><span class="s">"Timeout"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="n">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Start connecting to the remote peer |host:port| */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">initiate_connection</span><span class="p">(</span><span class="k">struct</span> <span class="n">event_base</span> <span class="o">*</span><span class="n">evbase</span><span class="p">,</span>
|
|
<span class="n">SSL_CTX</span> <span class="o">*</span><span class="n">ssl_ctx</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">host</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">port</span><span class="p">,</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span><span class="p">;</span>
|
|
<span class="n">SSL</span> <span class="o">*</span><span class="n">ssl</span><span class="p">;</span>
|
|
|
|
<span class="n">ssl</span> <span class="o">=</span> <span class="n">create_ssl</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">);</span>
|
|
<span class="n">bev</span> <span class="o">=</span> <span class="n">bufferevent_openssl_socket_new</span><span class="p">(</span><span class="n">evbase</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ssl</span><span class="p">,</span>
|
|
<span class="n">BUFFEREVENT_SSL_CONNECTING</span><span class="p">,</span>
|
|
<span class="n">BEV_OPT_DEFER_CALLBACKS</span> <span class="o">|</span>
|
|
<span class="n">BEV_OPT_CLOSE_ON_FREE</span><span class="p">);</span>
|
|
<span class="n">bufferevent_setcb</span><span class="p">(</span><span class="n">bev</span><span class="p">,</span> <span class="n">readcb</span><span class="p">,</span> <span class="n">writecb</span><span class="p">,</span> <span class="n">eventcb</span><span class="p">,</span> <span class="n">session_data</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">bufferevent_socket_connect_hostname</span><span class="p">(</span><span class="n">bev</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">dnsbase</span><span class="p">,</span>
|
|
<span class="n">AF_UNSPEC</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">);</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not connect to the remote host %s"</span><span class="p">,</span> <span class="n">host</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">bev</span> <span class="o">=</span> <span class="n">bev</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Get resource denoted by the |uri|. The debug and error messages are</span>
|
|
<span class="cm"> printed in stderr, while the response body is printed in stdout. */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">run</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">uri</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">http_parser_url</span> <span class="n">u</span><span class="p">;</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">host</span><span class="p">;</span>
|
|
<span class="kt">uint16_t</span> <span class="n">port</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="n">SSL_CTX</span> <span class="o">*</span><span class="n">ssl_ctx</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">event_base</span> <span class="o">*</span><span class="n">evbase</span><span class="p">;</span>
|
|
<span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">;</span>
|
|
|
|
<span class="cm">/* Parse the |uri| and stores its components in |u| */</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">http_parser_parse_url</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">uri</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&</span><span class="n">u</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="n">rv</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not parse URI %s"</span><span class="p">,</span> <span class="n">uri</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="n">host</span> <span class="o">=</span> <span class="n">strndup</span><span class="p">(</span><span class="o">&</span><span class="n">uri</span><span class="p">[</span><span class="n">u</span><span class="p">.</span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_HOST</span><span class="p">].</span><span class="n">off</span><span class="p">],</span> <span class="n">u</span><span class="p">.</span><span class="n">field_data</span><span class="p">[</span><span class="n">UF_HOST</span><span class="p">].</span><span class="n">len</span><span class="p">);</span>
|
|
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">u</span><span class="p">.</span><span class="n">field_set</span> <span class="o">&</span> <span class="p">(</span><span class="mi">1</span> <span class="o"><<</span> <span class="n">UF_PORT</span><span class="p">)))</span> <span class="p">{</span>
|
|
<span class="n">port</span> <span class="o">=</span> <span class="mi">443</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
|
|
<span class="n">port</span> <span class="o">=</span> <span class="n">u</span><span class="p">.</span><span class="n">port</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="n">ssl_ctx</span> <span class="o">=</span> <span class="n">create_ssl_ctx</span><span class="p">();</span>
|
|
|
|
<span class="n">evbase</span> <span class="o">=</span> <span class="n">event_base_new</span><span class="p">();</span>
|
|
|
|
<span class="n">session_data</span> <span class="o">=</span> <span class="n">create_http2_session_data</span><span class="p">(</span><span class="n">evbase</span><span class="p">);</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">stream_data</span> <span class="o">=</span> <span class="n">create_http2_stream_data</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="o">&</span><span class="n">u</span><span class="p">);</span>
|
|
|
|
<span class="n">initiate_connection</span><span class="p">(</span><span class="n">evbase</span><span class="p">,</span> <span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">session_data</span><span class="p">);</span>
|
|
<span class="n">free</span><span class="p">(</span><span class="n">host</span><span class="p">);</span>
|
|
<span class="n">host</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
|
|
|
<span class="n">event_base_loop</span><span class="p">(</span><span class="n">evbase</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
|
|
|
|
<span class="n">event_base_free</span><span class="p">(</span><span class="n">evbase</span><span class="p">);</span>
|
|
<span class="n">SSL_CTX_free</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">sigaction</span> <span class="n">act</span><span class="p">;</span>
|
|
|
|
<span class="k">if</span><span class="p">(</span><span class="n">argc</span> <span class="o"><</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Usage: libevent-client HTTPS_URI</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
|
<span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="n">memset</span><span class="p">(</span><span class="o">&</span><span class="n">act</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">sigaction</span><span class="p">));</span>
|
|
<span class="n">act</span><span class="p">.</span><span class="n">sa_handler</span> <span class="o">=</span> <span class="n">SIG_IGN</span><span class="p">;</span>
|
|
<span class="n">sigaction</span><span class="p">(</span><span class="n">SIGPIPE</span><span class="p">,</span> <span class="o">&</span><span class="n">act</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
|
|
|
|
<span class="n">SSL_load_error_strings</span><span class="p">();</span>
|
|
<span class="n">SSL_library_init</span><span class="p">();</span>
|
|
|
|
<span class="n">run</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
|
|
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
<footer>
|
|
|
|
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
|
|
|
<a href="tutorial-server.html" class="btn btn-neutral float-right" title="Tutorial: HTTP/2 server"/>Next <span class="fa fa-arrow-circle-right"></span></a>
|
|
|
|
|
|
<a href="package_README.html" class="btn btn-neutral" title="nghttp2 - HTTP/2 C Library"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
|
|
|
</div>
|
|
|
|
|
|
<hr/>
|
|
|
|
<div role="contentinfo">
|
|
<p>
|
|
© Copyright 2012, 2014, Tatsuhiro Tsujikawa.
|
|
</p>
|
|
</div>
|
|
|
|
<a href="https://github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
|
|
</body>
|
|
</html> |