1566 lines
182 KiB
HTML
1566 lines
182 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 server — nghttp2 0.7.8-DEV documentation</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link href='https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic|Roboto+Slab:400,700|Inconsolata:400,700&subset=latin,cyrillic' rel='stylesheet' type='text/css'>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
|
|
|
|
|
|
|
<link rel="top" title="nghttp2 0.7.8-DEV documentation" href="index.html"/>
|
|
<link rel="next" title="Tutorial: HPACK API" href="tutorial-hpack.html"/>
|
|
<link rel="prev" title="Tutorial: HTTP/2 client" href="tutorial-client.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#unit-tests">Unit tests</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#integration-tests">Integration tests</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#libnghttp2-asio-high-level-http-2-c-library">libnghttp2_asio: High level HTTP/2 C++ library</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#python-bindings">Python bindings</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="package_README.html#contribution">Contribution</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="contribute.html">Contribution Guidelines</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="contribute.html#coding-style">Coding style</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="building-android-binary.html">Building Android binary</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-client.html">Tutorial: HTTP/2 client</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="tutorial-client.html#libevent-client-c">libevent-client.c</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1 current"><a class="current reference internal" href="">Tutorial: HTTP/2 server</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="#libevent-server-c">libevent-server.c</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-hpack.html">Tutorial: HPACK API</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="tutorial-hpack.html#deflating-encoding-headers">Deflating (encoding) headers</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="tutorial-hpack.html#inflating-decoding-headers">Inflating (decoding) headers</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="tutorial-hpack.html#deflate-c">deflate.c</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="nghttp.1.html">nghttp(1)</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttp.1.html#synopsis">SYNOPSIS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttp.1.html#description">DESCRIPTION</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttp.1.html#options">OPTIONS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttp.1.html#see-also">SEE ALSO</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="nghttpd.1.html">nghttpd(1)</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpd.1.html#synopsis">SYNOPSIS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpd.1.html#description">DESCRIPTION</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpd.1.html#options">OPTIONS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpd.1.html#see-also">SEE ALSO</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="nghttpx.1.html">nghttpx(1)</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#synopsis">SYNOPSIS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#description">DESCRIPTION</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#options">OPTIONS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#files">FILES</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#signals">SIGNALS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#server-push">SERVER PUSH</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#unix-domain-socket">UNIX DOMAIN SOCKET</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx.1.html#see-also">SEE ALSO</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="h2load.1.html">h2load(1)</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load.1.html#synopsis">SYNOPSIS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load.1.html#description">DESCRIPTION</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load.1.html#options">OPTIONS</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load.1.html#output">OUTPUT</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load.1.html#see-also">SEE ALSO</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="nghttpx-howto.html">nghttpx - HTTP/2 proxy - 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>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#rewriting-location-header-field">Rewriting location header field</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#hot-swapping">Hot swapping</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#re-opening-log-files">Re-opening log files</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="nghttpx-howto.html#multiple-backend-addresses">Multiple backend addresses</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="h2load-howto.html">h2load - HTTP/2 benchmarking tool - HOW-TO</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load-howto.html#basic-usage">Basic Usage</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load-howto.html#flow-control">Flow Control</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load-howto.html#multi-threading">Multi-Threading</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load-howto.html#selecting-protocol-for-clear-text">Selecting protocol for clear text</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="h2load-howto.html#multiple-uris">Multiple URIs</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#http-messaging">HTTP Messaging</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="libnghttp2_asio.html">libnghttp2_asio: High level HTTP/2 C++ library</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="libnghttp2_asio.html#server-api">Server API</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="libnghttp2_asio.html#client-api">Client API</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="python-apiref.html">Python API Reference</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="python-apiref.html#hpack-api">HPACK API</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="python-apiref.html#http-2-servers">HTTP/2 servers</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 internal" href="asio_http2_server.h.html">asio_http2_server.h</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="asio_http2_client.h.html">asio_http2_client.h</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="asio_http2.h.html">asio_http2.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>
|
|
<li class="toctree-l1"><a class="reference external" href="https://nghttp2.org/">nghttp2.org</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 server</li>
|
|
<li class="wy-breadcrumbs-aside">
|
|
|
|
</li>
|
|
</ul>
|
|
<hr/>
|
|
</div>
|
|
<div role="main" class="document">
|
|
|
|
<div class="section" id="tutorial-http-2-server">
|
|
<h1>Tutorial: HTTP/2 server<a class="headerlink" href="#tutorial-http-2-server" title="Permalink to this headline">¶</a></h1>
|
|
<p>In this tutorial, we are going to write single-threaded, event-based
|
|
HTTP/2 web server, which supports HTTPS only. It can handle
|
|
concurrent multiple requests, but only the GET method is supported. The
|
|
complete source code, <a class="reference internal" href="#libevent-server-c">libevent-server.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 server takes 3 arguments, a port number to listen to, a path to
|
|
your SSL/TLS private key file and a path to your certificate file. Its
|
|
synopsis is like this:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre>$ libevent-server PORT /path/to/server.key /path/to/server.crt
|
|
</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 create a setup routine for libevent and OpenSSL in the functions
|
|
<tt class="docutils literal"><span class="pre">main()</span></tt> and <tt class="docutils literal"><span class="pre">run()</span></tt>. One thing in there you should look at, is the setup
|
|
of the NPN callback. The NPN callback is used for the server to advertise
|
|
which application protocols the server supports to a client. In this example
|
|
program, when creating <tt class="docutils literal"><span class="pre">SSL_CTX</span></tt> object, we store the application protocol
|
|
name in the wire format of NPN in a statically allocated buffer. This is safe
|
|
because we only create one <tt class="docutils literal"><span class="pre">SSL_CTX</span></tt> object in the program's entire life
|
|
time:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">next_proto_list</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
|
|
<span class="k">static</span> <span class="kt">size_t</span> <span class="n">next_proto_list_len</span><span class="p">;</span>
|
|
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">next_proto_cb</span><span class="p">(</span><span class="n">SSL</span> <span class="o">*</span><span class="n">s</span> <span class="n">_U_</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">data</span><span class="p">,</span>
|
|
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*</span><span class="n">len</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span> <span class="n">_U_</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">next_proto_list</span><span class="p">;</span>
|
|
<span class="o">*</span><span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">next_proto_list_len</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="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="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">key_file</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">cert_file</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">EC_KEY</span> <span class="o">*</span><span class="n">ecdh</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_server_method</span><span class="p">());</span>
|
|
|
|
<span class="p">...</span>
|
|
|
|
<span class="n">next_proto_list</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">NGHTTP2_PROTO_VERSION_ID_LEN</span><span class="p">;</span>
|
|
<span class="n">memcpy</span><span class="p">(</span><span class="o">&</span><span class="n">next_proto_list</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">NGHTTP2_PROTO_VERSION_ID</span><span class="p">,</span>
|
|
<span class="n">NGHTTP2_PROTO_VERSION_ID_LEN</span><span class="p">);</span>
|
|
<span class="n">next_proto_list_len</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">NGHTTP2_PROTO_VERSION_ID_LEN</span><span class="p">;</span>
|
|
|
|
<span class="n">SSL_CTX_set_next_protos_advertised_cb</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">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>The wire format of NPN is a sequence of length prefixed string. Exactly one
|
|
byte is used to specify the length of each protocol identifier. In this
|
|
tutorial, we advertise the specific HTTP/2 protocol version the current
|
|
nghttp2 library supports. The nghttp2 library exports its identifier in
|
|
<a class="reference internal" href="apiref.html#c.NGHTTP2_PROTO_VERSION_ID" title="NGHTTP2_PROTO_VERSION_ID"><tt class="xref c c-macro docutils literal"><span class="pre">NGHTTP2_PROTO_VERSION_ID</span></tt></a>. The <tt class="docutils literal"><span class="pre">next_proto_cb()</span></tt> function is the
|
|
server-side NPN callback. In the OpenSSL implementation, we just assign the
|
|
pointer to the NPN buffers we filled in earlier. The NPN callback function is
|
|
set to the <tt class="docutils literal"><span class="pre">SSL_CTX</span></tt> object using
|
|
<tt class="docutils literal"><span class="pre">SSL_CTX_set_next_protos_advertised_cb()</span></tt>.</p>
|
|
<p>We use the <tt class="docutils literal"><span class="pre">app_content</span></tt> structure to store application-wide data:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">struct</span> <span class="n">app_context</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="p">};</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We use the <tt class="docutils literal"><span class="pre">http2_session_data</span></tt> structure to store session-level
|
|
(which corresponds to one HTTP/2 connection) data:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">typedef</span> <span class="k">struct</span> <span class="n">http2_session_data</span> <span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">http2_stream_data</span> <span class="n">root</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">app_context</span> <span class="o">*</span><span class="n">app_ctx</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">char</span> <span class="o">*</span><span class="n">client_addr</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="n">http2_session_data</span><span class="p">;</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We use the <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> structure to store stream-level data:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">typedef</span> <span class="k">struct</span> <span class="n">http2_stream_data</span> <span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">prev</span><span class="p">,</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">request_path</span><span class="p">;</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">fd</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="n">http2_stream_data</span><span class="p">;</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>A single HTTP/2 session can have multiple streams. We manage these
|
|
multiple streams with a doubly linked list. The first element of this
|
|
list is pointed to by the <tt class="docutils literal"><span class="pre">root->next</span></tt> in <tt class="docutils literal"><span class="pre">http2_session_data</span></tt>.
|
|
Initially, <tt class="docutils literal"><span class="pre">root->next</span></tt> is <tt class="docutils literal"><span class="pre">NULL</span></tt>. We use libevent's bufferevent
|
|
structure to perform network I/O. Note that the bufferevent object is
|
|
kept in <tt class="docutils literal"><span class="pre">http2_session_data</span></tt> and not in <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt>. This
|
|
is because <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> is just a logical stream multiplexed
|
|
over the single connection managed by bufferevent in
|
|
<tt class="docutils literal"><span class="pre">http2_session_data</span></tt>.</p>
|
|
<p>We first create a listener object to accept incoming connections. We use
|
|
libevent's <tt class="docutils literal"><span class="pre">struct</span> <span class="pre">evconnlistener</span></tt> for this purpose:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">start_listen</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="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">service</span><span class="p">,</span>
|
|
<span class="n">app_context</span> <span class="o">*</span><span class="n">app_ctx</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">addrinfo</span> <span class="n">hints</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">addrinfo</span> <span class="o">*</span><span class="n">res</span><span class="p">,</span> <span class="o">*</span><span class="n">rp</span><span class="p">;</span>
|
|
|
|
<span class="n">memset</span><span class="p">(</span><span class="o">&</span><span class="n">hints</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">hints</span><span class="p">));</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_family</span> <span class="o">=</span> <span class="n">AF_UNSPEC</span><span class="p">;</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_socktype</span> <span class="o">=</span> <span class="n">SOCK_STREAM</span><span class="p">;</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_flags</span> <span class="o">=</span> <span class="n">AI_PASSIVE</span><span class="p">;</span>
|
|
<span class="cp">#ifdef AI_ADDRCONFIG</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_flags</span> <span class="o">|=</span> <span class="n">AI_ADDRCONFIG</span><span class="p">;</span>
|
|
<span class="cp">#endif </span><span class="cm">/* AI_ADDRCONFIG */</span><span class="cp"></span>
|
|
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">getaddrinfo</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">service</span><span class="p">,</span> <span class="o">&</span><span class="n">hints</span><span class="p">,</span> <span class="o">&</span><span class="n">res</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="nb">NULL</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="n">rp</span> <span class="o">=</span> <span class="n">res</span><span class="p">;</span> <span class="n">rp</span><span class="p">;</span> <span class="n">rp</span> <span class="o">=</span> <span class="n">rp</span><span class="o">-></span><span class="n">ai_next</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">evconnlistener</span> <span class="o">*</span><span class="n">listener</span><span class="p">;</span>
|
|
<span class="n">listener</span> <span class="o">=</span> <span class="n">evconnlistener_new_bind</span><span class="p">(</span>
|
|
<span class="n">evbase</span><span class="p">,</span> <span class="n">acceptcb</span><span class="p">,</span> <span class="n">app_ctx</span><span class="p">,</span> <span class="n">LEV_OPT_CLOSE_ON_FREE</span> <span class="o">|</span> <span class="n">LEV_OPT_REUSEABLE</span><span class="p">,</span>
|
|
<span class="mi">16</span><span class="p">,</span> <span class="n">rp</span><span class="o">-></span><span class="n">ai_addr</span><span class="p">,</span> <span class="n">rp</span><span class="o">-></span><span class="n">ai_addrlen</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">freeaddrinfo</span><span class="p">(</span><span class="n">res</span><span class="p">);</span>
|
|
|
|
<span class="k">return</span><span class="p">;</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 start listener"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>We specify the <tt class="docutils literal"><span class="pre">acceptcb</span></tt> callback which is called when a new connection is
|
|
accepted:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">acceptcb</span><span class="p">(</span><span class="k">struct</span> <span class="n">evconnlistener</span> <span class="o">*</span><span class="n">listener</span> <span class="n">_U_</span><span class="p">,</span> <span class="kt">int</span> <span class="n">fd</span><span class="p">,</span>
|
|
<span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="n">addr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">addrlen</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="n">app_context</span> <span class="o">*</span><span class="n">app_ctx</span> <span class="o">=</span> <span class="p">(</span><span class="n">app_context</span> <span class="o">*</span><span class="p">)</span><span class="n">arg</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="n">session_data</span> <span class="o">=</span> <span class="n">create_http2_session_data</span><span class="p">(</span><span class="n">app_ctx</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="n">addr</span><span class="p">,</span> <span class="n">addrlen</span><span class="p">);</span>
|
|
|
|
<span class="n">bufferevent_setcb</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">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="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Here we create the <tt class="docutils literal"><span class="pre">http2_session_data</span></tt> object. The bufferevent for
|
|
this connection is also initialized at this time. We specify three
|
|
callbacks for the bufferevent: <tt class="docutils literal"><span class="pre">readcb</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> callback is invoked by the 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="n">_U_</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="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s connected</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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="k">if</span> <span class="p">(</span><span class="n">send_server_connection_header</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="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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s EOF</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s network error</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s timeout</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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 the <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> events, we just simply tear down the connection.
|
|
The <tt class="docutils literal"><span class="pre">delete_http2_session_data()</span></tt> function destroys the
|
|
<tt class="docutils literal"><span class="pre">http2_session_data</span></tt> object and thus also its bufferevent member.
|
|
As a result, the underlying connection is closed. The
|
|
<tt class="docutils literal"><span class="pre">BEV_EVENT_CONNECTED</span></tt> event is invoked when SSL/TLS handshake is
|
|
finished successfully. Now we are ready to start the HTTP/2
|
|
communication.</p>
|
|
<p>We initialize a nghttp2 session object which is done in
|
|
<tt class="docutils literal"><span class="pre">initialize_nghttp2_session()</span></tt>:</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_option</span> <span class="o">*</span><span class="n">option</span><span class="p">;</span>
|
|
<span class="n">nghttp2_session_callbacks</span> <span class="o">*</span><span class="n">callbacks</span><span class="p">;</span>
|
|
|
|
<span class="n">nghttp2_option_new</span><span class="p">(</span><span class="o">&</span><span class="n">option</span><span class="p">);</span>
|
|
|
|
<span class="cm">/* Tells nghttp2_session object that it handles client connection</span>
|
|
<span class="cm"> preface */</span>
|
|
<span class="n">nghttp2_option_set_recv_client_preface</span><span class="p">(</span><span class="n">option</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_new</span><span class="p">(</span><span class="o">&</span><span class="n">callbacks</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_send_callback</span><span class="p">(</span><span class="n">callbacks</span><span class="p">,</span> <span class="n">send_callback</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_frame_recv_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="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_stream_close_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="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_header_callback</span><span class="p">(</span><span class="n">callbacks</span><span class="p">,</span>
|
|
<span class="n">on_header_callback</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_begin_headers_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="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_server_new2</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="n">callbacks</span><span class="p">,</span> <span class="n">session_data</span><span class="p">,</span>
|
|
<span class="n">option</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_del</span><span class="p">(</span><span class="n">callbacks</span><span class="p">);</span>
|
|
<span class="n">nghttp2_option_del</span><span class="p">(</span><span class="n">option</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>Since we are creating a server and uses options, the nghttp2 session
|
|
object is created using <a class="reference internal" href="apiref.html#c.nghttp2_session_server_new2" title="nghttp2_session_server_new2"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_session_server_new2()</span></tt></a> function. We
|
|
registers five callbacks for nghttp2 session object. We'll talk about
|
|
these callbacks later. Our server only speaks HTTP/2. In this case,
|
|
we use <a class="reference internal" href="apiref.html#c.nghttp2_option_set_recv_client_preface" title="nghttp2_option_set_recv_client_preface"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_option_set_recv_client_preface()</span></tt></a> to make
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_session" title="nghttp2_session"><tt class="xref c c-type docutils literal"><span class="pre">nghttp2_session</span></tt></a> object handle client connection preface, which
|
|
saves some lines of application code.</p>
|
|
<p>After initialization of the nghttp2 session object, we are going to send
|
|
a server connection header in <tt class="docutils literal"><span class="pre">send_server_connection_header()</span></tt>:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">int</span> <span class="nf">send_server_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="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_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">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 server connection header is a SETTINGS frame. We specify
|
|
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the 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 it does not actually send it. All
|
|
functions in the <tt class="docutils literal"><span class="pre">nghttp2_submit_*()</span></tt> family 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> should be used, as
|
|
described later.</p>
|
|
<p>Since bufferevent may buffer more than the first 24 bytes from the client, we
|
|
have to process them here since libevent won't invoke callback functions for
|
|
this pending data. To process the received data, we call the
|
|
<tt class="docutils literal"><span class="pre">session_recv()</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">session_recv</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">ssize_t</span> <span class="n">readlen</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">session_data</span><span class="o">-></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">readlen</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">readlen</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="kt">int</span><span class="p">)</span><span class="n">readlen</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">if</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">readlen</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">warnx</span><span class="p">(</span><span class="s">"Fatal error: evbuffer_drain failed"</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">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="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>In this function, we feed all unprocessed but already received data to the
|
|
nghttp2 session object using 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> 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> function processes the data and may invoke the
|
|
nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
|
outgoing frames, we call <tt class="docutils literal"><span class="pre">session_send()</span></tt> function to send off 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 calls <tt class="docutils literal"><span class="pre">send_callback()</span></tt> of type
|
|
<a class="reference internal" href="apiref.html#c.nghttp2_send_callback" title="nghttp2_send_callback"><tt class="xref c c-type docutils literal"><span class="pre">nghttp2_send_callback</span></tt></a>. The <tt class="docutils literal"><span class="pre">send_callback()</span></tt> 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="n">_U_</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="n">_U_</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="cm">/* Avoid excessive buffering in server side. */</span>
|
|
<span class="k">if</span> <span class="p">(</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="n">OUTPUT_WOULDBLOCK_THRESHOLD</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">NGHTTP2_ERR_WOULDBLOCK</span><span class="p">;</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 a non-blocking socket directly using <tt class="docutils literal"><span class="pre">write()</span></tt> system call
|
|
in the <tt class="docutils literal"><span class="pre">send_callback()</span></tt>, we would surely get <tt class="docutils literal"><span class="pre">EAGAIN</span></tt> or
|
|
<tt class="docutils literal"><span class="pre">EWOULDBLOCK</span></tt> back 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 when writing to the
|
|
bufferevent, we have to regulate the amount data to get buffered
|
|
ourselves to avoid using huge amounts of memory. To achieve this, we
|
|
check the size of the output buffer and if it reaches more than or
|
|
equal to <tt class="docutils literal"><span class="pre">OUTPUT_WOULDBLOCK_THRESHOLD</span></tt> bytes, we stop writing data
|
|
and 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 tell the library to stop
|
|
calling send_callback.</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="n">_U_</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">session_recv</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 just call <tt class="docutils literal"><span class="pre">session_recv()</span></tt> to process incoming
|
|
data.</p>
|
|
<p>The third bufferevent callback is <tt class="docutils literal"><span class="pre">writecb()</span></tt>, which is invoked when all
|
|
data in the bufferevent output buffer has 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">evbuffer_get_length</span><span class="p">(</span><span class="n">bufferevent_get_output</span><span class="p">(</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="k">return</span><span class="p">;</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="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="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>First we check whether we should drop the connection or not. The nghttp2
|
|
session object keeps track of reception and transmission of GOAWAY frames and
|
|
other error conditions as well. Using this information, the nghttp2 session
|
|
object will tell whether the connection should be dropped or not. More
|
|
specifically, if 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 left in the
|
|
connection. But since we are using bufferevent and its deferred callback
|
|
option, the bufferevent output buffer may contain pending data when the
|
|
<tt class="docutils literal"><span class="pre">writecb()</span></tt> is called. To handle this, we check whether the output buffer is
|
|
empty or not. If all these conditions are met, we drop connection.</p>
|
|
<p>Otherwise, we call <tt class="docutils literal"><span class="pre">session_send()</span></tt> to process the pending output
|
|
data. Remember that in <tt class="docutils literal"><span class="pre">send_callback()</span></tt>, we must not write all data to
|
|
bufferevent to avoid excessive buffering. We continue processing pending data
|
|
when the output buffer becomes empty.</p>
|
|
<p>We have already described the nghttp2 callback <tt class="docutils literal"><span class="pre">send_callback()</span></tt>. Let's
|
|
learn about the remaining nghttp2 callbacks we setup in
|
|
<tt class="docutils literal"><span class="pre">initialize_nghttp2_setup()</span></tt> function.</p>
|
|
<p>The <tt class="docutils literal"><span class="pre">on_begin_headers_callback()</span></tt> function is invoked when the reception of
|
|
a header block in HEADERS or PUSH_PROMISE frame is started:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><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="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="k">return</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">create_http2_stream_data</span><span class="p">(</span><span class="n">session_data</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="n">nghttp2_session_set_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="n">stream_data</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>We are only interested in the HEADERS frame in this function. Since the
|
|
HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a
|
|
request HEADERS, which opens new stream. If the frame is a request HEADERS, we
|
|
create a <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> object to store the stream related data. We
|
|
associate the created <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> object with the stream in the
|
|
nghttp2 session object using <tt class="xref c c-func docutils literal"><span class="pre">nghttp2_set_stream_user_data()</span></tt> to get the
|
|
object without searching through the doubly linked list.</p>
|
|
<p>In this example server, we want to serve files relative to the current working
|
|
directory in which the program was invoked. Each header name/value pair is
|
|
emitted via <tt class="docutils literal"><span class="pre">on_header_callback</span></tt> function, which is called after
|
|
<tt class="docutils literal"><span class="pre">on_begin_headers_callback()</span></tt>:</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="n">_U_</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span> <span class="n">_U_</span><span class="p">)</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">const</span> <span class="kt">char</span> <span class="n">PATH</span><span class="p">[]</span> <span class="o">=</span> <span class="s">":path"</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="nl">NGHTTP2_HEADERS</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">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="k">break</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="n">stream_data</span> <span class="o">=</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="o">!</span><span class="n">stream_data</span> <span class="o">||</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</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">if</span> <span class="p">(</span><span class="n">namelen</span> <span class="o">==</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">PATH</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&&</span> <span class="n">memcmp</span><span class="p">(</span><span class="n">PATH</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="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="kt">size_t</span> <span class="n">j</span><span class="p">;</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o"><</span> <span class="n">valuelen</span> <span class="o">&&</span> <span class="n">value</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'?'</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span>
|
|
<span class="p">;</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</span> <span class="o">=</span> <span class="n">percent_decode</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">j</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>We search for the <tt class="docutils literal"><span class="pre">:path</span></tt> header field among the request headers and store
|
|
the requested path in the <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> object. In this example
|
|
program, we ignore <tt class="docutils literal"><span class="pre">:method</span></tt> header field and always treat the request as a
|
|
GET request.</p>
|
|
<p>The <tt class="docutils literal"><span class="pre">on_frame_recv_callback()</span></tt> function is invoked when a frame is
|
|
fully received:</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="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_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="nl">NGHTTP2_DATA</span><span class="p">:</span>
|
|
<span class="k">case</span> <span class="nl">NGHTTP2_HEADERS</span><span class="p">:</span>
|
|
<span class="cm">/* Check that the client request has finished */</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">flags</span> <span class="o">&</span> <span class="n">NGHTTP2_FLAG_END_STREAM</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span> <span class="o">=</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="cm">/* For DATA and HEADERS frame, this callback may be called after</span>
|
|
<span class="cm"> on_stream_close_callback. Check that stream still alive. */</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">stream_data</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="k">return</span> <span class="n">on_request_recv</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">session_data</span><span class="p">,</span> <span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">break</span><span class="p">;</span>
|
|
<span class="k">default</span><span class="o">:</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>First we retrieve the <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> object associated with the stream
|
|
in <tt class="docutils literal"><span class="pre">on_begin_headers_callback()</span></tt>. It is done 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>. If the requested path cannot be
|
|
served for some reason (e.g., file is not found), we send a 404 response,
|
|
which is done in <tt class="docutils literal"><span class="pre">error_reply()</span></tt>. Otherwise, we open the requested file and
|
|
send its content. We send the header field <tt class="docutils literal"><span class="pre">:status</span></tt> as a single response
|
|
header.</p>
|
|
<p>Sending the content of the file is done in <tt class="docutils literal"><span class="pre">send_response()</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">send_response</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_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="kt">int</span> <span class="n">fd</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">nghttp2_data_provider</span> <span class="n">data_prd</span><span class="p">;</span>
|
|
<span class="n">data_prd</span><span class="p">.</span><span class="n">source</span><span class="p">.</span><span class="n">fd</span> <span class="o">=</span> <span class="n">fd</span><span class="p">;</span>
|
|
<span class="n">data_prd</span><span class="p">.</span><span class="n">read_callback</span> <span class="o">=</span> <span class="n">file_read_callback</span><span class="p">;</span>
|
|
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_submit_response</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">stream_id</span><span class="p">,</span> <span class="n">nva</span><span class="p">,</span> <span class="n">nvlen</span><span class="p">,</span> <span class="o">&</span><span class="n">data_prd</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 nghttp2 library uses the <a class="reference internal" href="apiref.html#c.nghttp2_data_provider" title="nghttp2_data_provider"><tt class="xref c c-type docutils literal"><span class="pre">nghttp2_data_provider</span></tt></a> structure to
|
|
send entity body to the remote peer. The <tt class="docutils literal"><span class="pre">source</span></tt> member of this
|
|
structure is a union and it can be either void pointer or int which is
|
|
intended to be used as file descriptor. In this example server, we use
|
|
the file descriptor. We also set the <tt class="docutils literal"><span class="pre">file_read_callback()</span></tt> callback
|
|
function to read the contents of the file:</p>
|
|
<div class="highlight-c"><div class="highlight"><pre><span class="k">static</span> <span class="kt">ssize_t</span> <span class="nf">file_read_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span> <span class="n">_U_</span><span class="p">,</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span> <span class="n">_U_</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span>
|
|
<span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">data_flags</span><span class="p">,</span>
|
|
<span class="n">nghttp2_data_source</span> <span class="o">*</span><span class="n">source</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span> <span class="n">_U_</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">source</span><span class="o">-></span><span class="n">fd</span><span class="p">;</span>
|
|
<span class="kt">ssize_t</span> <span class="n">r</span><span class="p">;</span>
|
|
<span class="k">while</span> <span class="p">((</span><span class="n">r</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">length</span><span class="p">))</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="o">&&</span> <span class="n">errno</span> <span class="o">==</span> <span class="n">EINTR</span><span class="p">)</span>
|
|
<span class="p">;</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">r</span> <span class="o">==</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="n">NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">r</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="o">*</span><span class="n">data_flags</span> <span class="o">|=</span> <span class="n">NGHTTP2_DATA_FLAG_EOF</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="n">r</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
<p>If an error happens while reading the file, we return
|
|
<a class="reference internal" href="apiref.html#c.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE" title="NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE"><tt class="xref c c-macro docutils literal"><span class="pre">NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE</span></tt></a>. This tells the
|
|
library to send RST_STREAM to the stream. When all data has been read, set
|
|
the <a class="reference internal" href="apiref.html#c.NGHTTP2_DATA_FLAG_EOF" title="NGHTTP2_DATA_FLAG_EOF"><tt class="xref c c-macro docutils literal"><span class="pre">NGHTTP2_DATA_FLAG_EOF</span></tt></a> flag to <tt class="docutils literal"><span class="pre">*data_flags</span></tt> to tell the
|
|
nghttp2 library that we have finished reading the file.</p>
|
|
<p>The <a class="reference internal" href="apiref.html#c.nghttp2_submit_response" title="nghttp2_submit_response"><tt class="xref c c-func docutils literal"><span class="pre">nghttp2_submit_response()</span></tt></a> function is used to send the response to the
|
|
remote peer.</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="kt">uint32_t</span> <span class="n">error_code</span> <span class="n">_U_</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="n">stream_data</span> <span class="o">=</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">stream_id</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">stream_data</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="n">remove_stream</span><span class="p">(</span><span class="n">session_data</span><span class="p">,</span> <span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="n">delete_http2_stream_data</span><span class="p">(</span><span class="n">stream_data</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>We destroy the <tt class="docutils literal"><span class="pre">http2_stream_data</span></tt> object in this function since the stream
|
|
is about to close and we no longer use that object.</p>
|
|
<div class="section" id="libevent-server-c">
|
|
<h2>libevent-server.c<a class="headerlink" href="#libevent-server-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">#ifdef HAVE_CONFIG_H</span>
|
|
<span class="cp">#include <config.h></span>
|
|
<span class="cp">#endif </span><span class="cm">/* !HAVE_CONFIG_H */</span><span class="cp"></span>
|
|
|
|
<span class="cp">#include <sys/types.h></span>
|
|
<span class="cp">#include <sys/socket.h></span>
|
|
<span class="cp">#include <netdb.h></span>
|
|
<span class="cp">#include <signal.h></span>
|
|
<span class="cp">#include <unistd.h></span>
|
|
<span class="cp">#include <sys/stat.h></span>
|
|
<span class="cp">#include <fcntl.h></span>
|
|
<span class="cp">#include <ctype.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 <openssl/ssl.h></span>
|
|
<span class="cp">#include <openssl/err.h></span>
|
|
<span class="cp">#include <openssl/conf.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/listener.h></span>
|
|
|
|
<span class="cp">#include <nghttp2/nghttp2.h></span>
|
|
|
|
<span class="cp">#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)</span>
|
|
|
|
<span class="cp">#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))</span>
|
|
|
|
<span class="cp">#define MAKE_NV(NAME, VALUE) \</span>
|
|
<span class="cp"> { \</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="cp"> }</span>
|
|
|
|
<span class="k">struct</span> <span class="n">app_context</span><span class="p">;</span>
|
|
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">app_context</span> <span class="n">app_context</span><span class="p">;</span>
|
|
|
|
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">http2_stream_data</span> <span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">prev</span><span class="p">,</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">request_path</span><span class="p">;</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">fd</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="n">http2_session_data</span> <span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">http2_stream_data</span> <span class="n">root</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">app_context</span> <span class="o">*</span><span class="n">app_ctx</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">char</span> <span class="o">*</span><span class="n">client_addr</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="n">http2_session_data</span><span class="p">;</span>
|
|
|
|
<span class="k">struct</span> <span class="n">app_context</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="p">};</span>
|
|
|
|
<span class="k">static</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">next_proto_list</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
|
|
<span class="k">static</span> <span class="kt">size_t</span> <span class="n">next_proto_list_len</span><span class="p">;</span>
|
|
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">next_proto_cb</span><span class="p">(</span><span class="n">SSL</span> <span class="o">*</span><span class="n">s</span> <span class="n">_U_</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">data</span><span class="p">,</span>
|
|
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="o">*</span><span class="n">len</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span> <span class="n">_U_</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">next_proto_list</span><span class="p">;</span>
|
|
<span class="o">*</span><span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">next_proto_list_len</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="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">key_file</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">cert_file</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">EC_KEY</span> <span class="o">*</span><span class="n">ecdh</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_server_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_SSLv3</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">ecdh</span> <span class="o">=</span> <span class="n">EC_KEY_new_by_curve_name</span><span class="p">(</span><span class="n">NID_X9_62_prime256v1</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ecdh</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">"EC_KEY_new_by_curv_name failed: %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_tmp_ecdh</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">ecdh</span><span class="p">);</span>
|
|
<span class="n">EC_KEY_free</span><span class="p">(</span><span class="n">ecdh</span><span class="p">);</span>
|
|
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">SSL_CTX_use_PrivateKey_file</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">key_file</span><span class="p">,</span> <span class="n">SSL_FILETYPE_PEM</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</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 read private key file %s"</span><span class="p">,</span> <span class="n">key_file</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">SSL_CTX_use_certificate_chain_file</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">cert_file</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</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 read certificate file %s"</span><span class="p">,</span> <span class="n">cert_file</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="n">next_proto_list</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">NGHTTP2_PROTO_VERSION_ID_LEN</span><span class="p">;</span>
|
|
<span class="n">memcpy</span><span class="p">(</span><span class="o">&</span><span class="n">next_proto_list</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">NGHTTP2_PROTO_VERSION_ID</span><span class="p">,</span>
|
|
<span class="n">NGHTTP2_PROTO_VERSION_ID_LEN</span><span class="p">);</span>
|
|
<span class="n">next_proto_list_len</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">NGHTTP2_PROTO_VERSION_ID_LEN</span><span class="p">;</span>
|
|
|
|
<span class="n">SSL_CTX_set_next_protos_advertised_cb</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">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">add_stream</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="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">stream_data</span><span class="o">-></span><span class="n">next</span> <span class="o">=</span> <span class="n">session_data</span><span class="o">-></span><span class="n">root</span><span class="p">.</span><span class="n">next</span><span class="p">;</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">root</span><span class="p">.</span><span class="n">next</span> <span class="o">=</span> <span class="n">stream_data</span><span class="p">;</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">prev</span> <span class="o">=</span> <span class="o">&</span><span class="n">session_data</span><span class="o">-></span><span class="n">root</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">next</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">next</span><span class="o">-></span><span class="n">prev</span> <span class="o">=</span> <span class="n">stream_data</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">remove_stream</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="n">_U_</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">stream_data</span><span class="o">-></span><span class="n">prev</span><span class="o">-></span><span class="n">next</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">next</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">next</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">next</span><span class="o">-></span><span class="n">prev</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">prev</span><span class="p">;</span>
|
|
<span class="p">}</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="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">,</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="o">*</span><span class="n">stream_data</span><span class="p">;</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">memset</span><span class="p">(</span><span class="n">stream_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_stream_data</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">stream_id</span><span class="p">;</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">fd</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
|
|
|
|
<span class="n">add_stream</span><span class="p">(</span><span class="n">session_data</span><span class="p">,</span> <span class="n">stream_data</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="k">if</span> <span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">fd</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">close</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">fd</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">request_path</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="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="n">app_context</span> <span class="o">*</span><span class="n">app_ctx</span><span class="p">,</span>
|
|
<span class="kt">int</span> <span class="n">fd</span><span class="p">,</span>
|
|
<span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="n">addr</span><span class="p">,</span>
|
|
<span class="kt">int</span> <span class="n">addrlen</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_session_data</span> <span class="o">*</span><span class="n">session_data</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">char</span> <span class="n">host</span><span class="p">[</span><span class="n">NI_MAXHOST</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">ssl</span> <span class="o">=</span> <span class="n">create_ssl</span><span class="p">(</span><span class="n">app_ctx</span><span class="o">-></span><span class="n">ssl_ctx</span><span class="p">);</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">app_ctx</span> <span class="o">=</span> <span class="n">app_ctx</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">session_data</span><span class="o">-></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">app_ctx</span><span class="o">-></span><span class="n">evbase</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="n">ssl</span><span class="p">,</span> <span class="n">BUFFEREVENT_SSL_ACCEPTING</span><span class="p">,</span>
|
|
<span class="n">BEV_OPT_CLOSE_ON_FREE</span> <span class="o">|</span> <span class="n">BEV_OPT_DEFER_CALLBACKS</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">getnameinfo</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">addrlen</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">host</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NI_NUMERICHOST</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">session_data</span><span class="o">-></span><span class="n">client_addr</span> <span class="o">=</span> <span class="n">strdup</span><span class="p">(</span><span class="s">"(unknown)"</span><span class="p">);</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
|
|
<span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</span> <span class="o">=</span> <span class="n">strdup</span><span class="p">(</span><span class="n">host</span><span class="p">);</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">http2_stream_data</span> <span class="o">*</span><span class="n">stream_data</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="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s disconnected</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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">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="k">for</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">root</span><span class="p">.</span><span class="n">next</span><span class="p">;</span> <span class="n">stream_data</span><span class="p">;)</span> <span class="p">{</span>
|
|
<span class="n">http2_stream_data</span> <span class="o">*</span><span class="n">next</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">next</span><span class="p">;</span>
|
|
<span class="n">delete_http2_stream_data</span><span class="p">(</span><span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="n">stream_data</span> <span class="o">=</span> <span class="n">next</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="o">-></span><span class="n">client_addr</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="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">/* Read the data in the bufferevent and feed them into nghttp2 library</span>
|
|
<span class="cm"> function. Invocation of nghttp2_session_mem_recv() may make</span>
|
|
<span class="cm"> additional pending frames, so call session_send() at the end of the</span>
|
|
<span class="cm"> function. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">session_recv</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">ssize_t</span> <span class="n">readlen</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">session_data</span><span class="o">-></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">readlen</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">readlen</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="kt">int</span><span class="p">)</span><span class="n">readlen</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">if</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">readlen</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">warnx</span><span class="p">(</span><span class="s">"Fatal error: evbuffer_drain failed"</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">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="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="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="n">_U_</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="n">_U_</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="cm">/* Avoid excessive buffering in server side. */</span>
|
|
<span class="k">if</span> <span class="p">(</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="n">OUTPUT_WOULDBLOCK_THRESHOLD</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">NGHTTP2_ERR_WOULDBLOCK</span><span class="p">;</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">/* Returns nonzero if the string |s| ends with the substring |sub| */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">ends_with</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">sub</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="kt">size_t</span> <span class="n">slen</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
|
|
<span class="kt">size_t</span> <span class="n">sublen</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">sub</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">slen</span> <span class="o"><</span> <span class="n">sublen</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="k">return</span> <span class="n">memcmp</span><span class="p">(</span><span class="n">s</span> <span class="o">+</span> <span class="n">slen</span> <span class="o">-</span> <span class="n">sublen</span><span class="p">,</span> <span class="n">sub</span><span class="p">,</span> <span class="n">sublen</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Returns int value of hex string character |c| */</span>
|
|
<span class="k">static</span> <span class="kt">uint8_t</span> <span class="nf">hex_to_uint</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="sc">'0'</span> <span class="o"><=</span> <span class="n">c</span> <span class="o">&&</span> <span class="n">c</span> <span class="o"><=</span> <span class="sc">'9'</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">c</span> <span class="o">-</span> <span class="sc">'0'</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="sc">'A'</span> <span class="o"><=</span> <span class="n">c</span> <span class="o">&&</span> <span class="n">c</span> <span class="o"><=</span> <span class="sc">'F'</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">c</span> <span class="o">-</span> <span class="sc">'A'</span> <span class="o">+</span> <span class="mi">10</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="sc">'a'</span> <span class="o"><=</span> <span class="n">c</span> <span class="o">&&</span> <span class="n">c</span> <span class="o"><=</span> <span class="sc">'f'</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">return</span> <span class="n">c</span> <span class="o">-</span> <span class="sc">'a'</span> <span class="o">+</span> <span class="mi">10</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">/* Decodes percent-encoded byte string |value| with length |valuelen|</span>
|
|
<span class="cm"> and returns the decoded byte string in allocated buffer. The return</span>
|
|
<span class="cm"> value is NULL terminated. The caller must free the returned</span>
|
|
<span class="cm"> string. */</span>
|
|
<span class="k">static</span> <span class="kt">char</span> <span class="o">*</span><span class="nf">percent_decode</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="kt">char</span> <span class="o">*</span><span class="n">res</span><span class="p">;</span>
|
|
|
|
<span class="n">res</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">valuelen</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">valuelen</span> <span class="o">></span> <span class="mi">3</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="n">j</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">j</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">valuelen</span> <span class="o">-</span> <span class="mi">2</span><span class="p">;)</span> <span class="p">{</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'%'</span> <span class="o">||</span> <span class="o">!</span><span class="n">isxdigit</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span> <span class="o">||</span>
|
|
<span class="o">!</span><span class="n">isxdigit</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]))</span> <span class="p">{</span>
|
|
<span class="n">res</span><span class="p">[</span><span class="n">j</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">[</span><span class="n">i</span><span class="o">++</span><span class="p">];</span>
|
|
<span class="k">continue</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="n">res</span><span class="p">[</span><span class="n">j</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">hex_to_uint</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span> <span class="o"><<</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="n">hex_to_uint</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]);</span>
|
|
<span class="n">i</span> <span class="o">+=</span> <span class="mi">3</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="n">memcpy</span><span class="p">(</span><span class="o">&</span><span class="n">res</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="o">&</span><span class="n">value</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">2</span><span class="p">);</span>
|
|
<span class="n">res</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
|
|
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
|
|
<span class="n">memcpy</span><span class="p">(</span><span class="n">res</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="n">res</span><span class="p">[</span><span class="n">valuelen</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="n">res</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">ssize_t</span> <span class="nf">file_read_callback</span><span class="p">(</span><span class="n">nghttp2_session</span> <span class="o">*</span><span class="n">session</span> <span class="n">_U_</span><span class="p">,</span>
|
|
<span class="kt">int32_t</span> <span class="n">stream_id</span> <span class="n">_U_</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span>
|
|
<span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">data_flags</span><span class="p">,</span>
|
|
<span class="n">nghttp2_data_source</span> <span class="o">*</span><span class="n">source</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span> <span class="n">_U_</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">source</span><span class="o">-></span><span class="n">fd</span><span class="p">;</span>
|
|
<span class="kt">ssize_t</span> <span class="n">r</span><span class="p">;</span>
|
|
<span class="k">while</span> <span class="p">((</span><span class="n">r</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">length</span><span class="p">))</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="o">&&</span> <span class="n">errno</span> <span class="o">==</span> <span class="n">EINTR</span><span class="p">)</span>
|
|
<span class="p">;</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">r</span> <span class="o">==</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="n">NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">r</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="o">*</span><span class="n">data_flags</span> <span class="o">|=</span> <span class="n">NGHTTP2_DATA_FLAG_EOF</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="k">return</span> <span class="n">r</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">send_response</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_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="kt">int</span> <span class="n">fd</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">nghttp2_data_provider</span> <span class="n">data_prd</span><span class="p">;</span>
|
|
<span class="n">data_prd</span><span class="p">.</span><span class="n">source</span><span class="p">.</span><span class="n">fd</span> <span class="o">=</span> <span class="n">fd</span><span class="p">;</span>
|
|
<span class="n">data_prd</span><span class="p">.</span><span class="n">read_callback</span> <span class="o">=</span> <span class="n">file_read_callback</span><span class="p">;</span>
|
|
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_submit_response</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">stream_id</span><span class="p">,</span> <span class="n">nva</span><span class="p">,</span> <span class="n">nvlen</span><span class="p">,</span> <span class="o">&</span><span class="n">data_prd</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="k">const</span> <span class="kt">char</span> <span class="n">ERROR_HTML</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"<html><head><title>404</title></head>"</span>
|
|
<span class="s">"<body><h1>404 Not Found</h1></body></html>"</span><span class="p">;</span>
|
|
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">error_reply</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="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="kt">int</span> <span class="n">rv</span><span class="p">;</span>
|
|
<span class="kt">ssize_t</span> <span class="n">writelen</span><span class="p">;</span>
|
|
<span class="kt">int</span> <span class="n">pipefd</span><span class="p">[</span><span class="mi">2</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_NV</span><span class="p">(</span><span class="s">":status"</span><span class="p">,</span> <span class="s">"404"</span><span class="p">)};</span>
|
|
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">pipe</span><span class="p">(</span><span class="n">pipefd</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">warn</span><span class="p">(</span><span class="s">"Could not create pipe"</span><span class="p">);</span>
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_submit_rst_stream</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">NGHTTP2_FLAG_NONE</span><span class="p">,</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</span><span class="p">,</span>
|
|
<span class="n">NGHTTP2_INTERNAL_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="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="n">writelen</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">pipefd</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">ERROR_HTML</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ERROR_HTML</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
|
|
<span class="n">close</span><span class="p">(</span><span class="n">pipefd</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
|
|
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">writelen</span> <span class="o">!=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ERROR_HTML</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">close</span><span class="p">(</span><span class="n">pipefd</span><span class="p">[</span><span class="mi">0</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="n">stream_data</span><span class="o">-></span><span class="n">fd</span> <span class="o">=</span> <span class="n">pipefd</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
|
|
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">send_response</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</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">pipefd</span><span class="p">[</span><span class="mi">0</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">close</span><span class="p">(</span><span class="n">pipefd</span><span class="p">[</span><span class="mi">0</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">/* 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="n">_U_</span><span class="p">,</span>
|
|
<span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span> <span class="n">_U_</span><span class="p">)</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">const</span> <span class="kt">char</span> <span class="n">PATH</span><span class="p">[]</span> <span class="o">=</span> <span class="s">":path"</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="nl">NGHTTP2_HEADERS</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">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="k">break</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
<span class="n">stream_data</span> <span class="o">=</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="o">!</span><span class="n">stream_data</span> <span class="o">||</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</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">if</span> <span class="p">(</span><span class="n">namelen</span> <span class="o">==</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">PATH</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&&</span> <span class="n">memcmp</span><span class="p">(</span><span class="n">PATH</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="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="kt">size_t</span> <span class="n">j</span><span class="p">;</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o"><</span> <span class="n">valuelen</span> <span class="o">&&</span> <span class="n">value</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'?'</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span>
|
|
<span class="p">;</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</span> <span class="o">=</span> <span class="n">percent_decode</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">j</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="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="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="k">return</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">create_http2_stream_data</span><span class="p">(</span><span class="n">session_data</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="n">nghttp2_session_set_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="n">stream_data</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">/* Minimum check for directory traversal. Returns nonzero if it is</span>
|
|
<span class="cm"> safe. */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">check_path</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="cm">/* We don't like '\' in url. */</span>
|
|
<span class="k">return</span> <span class="n">path</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&&</span> <span class="n">path</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'/'</span> <span class="o">&&</span> <span class="n">strchr</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sc">'\\'</span><span class="p">)</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">&&</span>
|
|
<span class="n">strstr</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">"/../"</span><span class="p">)</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">&&</span> <span class="n">strstr</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">"/./"</span><span class="p">)</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">&&</span>
|
|
<span class="o">!</span><span class="n">ends_with</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">"/.."</span><span class="p">)</span> <span class="o">&&</span> <span class="o">!</span><span class="n">ends_with</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">"/."</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">on_request_recv</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="n">http2_session_data</span> <span class="o">*</span><span class="n">session_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="kt">int</span> <span class="n">fd</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_NV</span><span class="p">(</span><span class="s">":status"</span><span class="p">,</span> <span class="s">"200"</span><span class="p">)};</span>
|
|
<span class="kt">char</span> <span class="o">*</span><span class="n">rel_path</span><span class="p">;</span>
|
|
|
|
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">error_reply</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">stream_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="k">return</span> <span class="n">NGHTTP2_ERR_CALLBACK_FAILURE</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="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s GET %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</span><span class="p">,</span>
|
|
<span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">check_path</span><span class="p">(</span><span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</span><span class="p">))</span> <span class="p">{</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">error_reply</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">stream_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="k">return</span> <span class="n">NGHTTP2_ERR_CALLBACK_FAILURE</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="k">for</span> <span class="p">(</span><span class="n">rel_path</span> <span class="o">=</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">request_path</span><span class="p">;</span> <span class="o">*</span><span class="n">rel_path</span> <span class="o">==</span> <span class="sc">'/'</span><span class="p">;</span> <span class="o">++</span><span class="n">rel_path</span><span class="p">)</span>
|
|
<span class="p">;</span>
|
|
<span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">rel_path</span><span class="p">,</span> <span class="n">O_RDONLY</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">==</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">error_reply</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">stream_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="k">return</span> <span class="n">NGHTTP2_ERR_CALLBACK_FAILURE</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="n">stream_data</span><span class="o">-></span><span class="n">fd</span> <span class="o">=</span> <span class="n">fd</span><span class="p">;</span>
|
|
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">send_response</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">stream_data</span><span class="o">-></span><span class="n">stream_id</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">fd</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">close</span><span class="p">(</span><span class="n">fd</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="k">return</span> <span class="mi">0</span><span class="p">;</span>
|
|
<span class="p">}</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="n">http2_stream_data</span> <span class="o">*</span><span class="n">stream_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="nl">NGHTTP2_DATA</span><span class="p">:</span>
|
|
<span class="k">case</span> <span class="nl">NGHTTP2_HEADERS</span><span class="p">:</span>
|
|
<span class="cm">/* Check that the client request has finished */</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">flags</span> <span class="o">&</span> <span class="n">NGHTTP2_FLAG_END_STREAM</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">stream_data</span> <span class="o">=</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="cm">/* For DATA and HEADERS frame, this callback may be called after</span>
|
|
<span class="cm"> on_stream_close_callback. Check that stream still alive. */</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">stream_data</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="k">return</span> <span class="n">on_request_recv</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">session_data</span><span class="p">,</span> <span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">break</span><span class="p">;</span>
|
|
<span class="k">default</span><span class="o">:</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="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="kt">uint32_t</span> <span class="n">error_code</span> <span class="n">_U_</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="n">stream_data</span> <span class="o">=</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">stream_id</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">stream_data</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="n">remove_stream</span><span class="p">(</span><span class="n">session_data</span><span class="p">,</span> <span class="n">stream_data</span><span class="p">);</span>
|
|
<span class="n">delete_http2_stream_data</span><span class="p">(</span><span class="n">stream_data</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="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_option</span> <span class="o">*</span><span class="n">option</span><span class="p">;</span>
|
|
<span class="n">nghttp2_session_callbacks</span> <span class="o">*</span><span class="n">callbacks</span><span class="p">;</span>
|
|
|
|
<span class="n">nghttp2_option_new</span><span class="p">(</span><span class="o">&</span><span class="n">option</span><span class="p">);</span>
|
|
|
|
<span class="cm">/* Tells nghttp2_session object that it handles client connection</span>
|
|
<span class="cm"> preface */</span>
|
|
<span class="n">nghttp2_option_set_recv_client_preface</span><span class="p">(</span><span class="n">option</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_new</span><span class="p">(</span><span class="o">&</span><span class="n">callbacks</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_send_callback</span><span class="p">(</span><span class="n">callbacks</span><span class="p">,</span> <span class="n">send_callback</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_frame_recv_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="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_stream_close_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="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_header_callback</span><span class="p">(</span><span class="n">callbacks</span><span class="p">,</span>
|
|
<span class="n">on_header_callback</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_set_on_begin_headers_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="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_server_new2</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="n">callbacks</span><span class="p">,</span> <span class="n">session_data</span><span class="p">,</span>
|
|
<span class="n">option</span><span class="p">);</span>
|
|
|
|
<span class="n">nghttp2_session_callbacks_del</span><span class="p">(</span><span class="n">callbacks</span><span class="p">);</span>
|
|
<span class="n">nghttp2_option_del</span><span class="p">(</span><span class="n">option</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="cm">/* Send HTTP/2 client connection header, which includes 24 bytes</span>
|
|
<span class="cm"> magic octets and SETTINGS frame */</span>
|
|
<span class="k">static</span> <span class="kt">int</span> <span class="nf">send_server_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="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_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">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 after client connection header was</span>
|
|
<span class="cm"> checked. */</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="n">_U_</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">session_recv</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. If the</span>
|
|
<span class="cm"> connection is not going to shutdown, we call session_send() to</span>
|
|
<span class="cm"> process pending data in the output buffer. This is necessary</span>
|
|
<span class="cm"> because we have a threshold on the buffer size to avoid too much</span>
|
|
<span class="cm"> buffering. See send_callback(). */</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">evbuffer_get_length</span><span class="p">(</span><span class="n">bufferevent_get_output</span><span class="p">(</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="k">return</span><span class="p">;</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="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="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">/* eventcb for bufferevent */</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="n">_U_</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="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s connected</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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="k">if</span> <span class="p">(</span><span class="n">send_server_connection_header</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="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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s EOF</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s network error</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s timeout</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">session_data</span><span class="o">-></span><span class="n">client_addr</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">/* callback for evconnlistener */</span>
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">acceptcb</span><span class="p">(</span><span class="k">struct</span> <span class="n">evconnlistener</span> <span class="o">*</span><span class="n">listener</span> <span class="n">_U_</span><span class="p">,</span> <span class="kt">int</span> <span class="n">fd</span><span class="p">,</span>
|
|
<span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="n">addr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">addrlen</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="n">app_context</span> <span class="o">*</span><span class="n">app_ctx</span> <span class="o">=</span> <span class="p">(</span><span class="n">app_context</span> <span class="o">*</span><span class="p">)</span><span class="n">arg</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="n">session_data</span> <span class="o">=</span> <span class="n">create_http2_session_data</span><span class="p">(</span><span class="n">app_ctx</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="n">addr</span><span class="p">,</span> <span class="n">addrlen</span><span class="p">);</span>
|
|
|
|
<span class="n">bufferevent_setcb</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">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="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">start_listen</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="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">service</span><span class="p">,</span>
|
|
<span class="n">app_context</span> <span class="o">*</span><span class="n">app_ctx</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">addrinfo</span> <span class="n">hints</span><span class="p">;</span>
|
|
<span class="k">struct</span> <span class="n">addrinfo</span> <span class="o">*</span><span class="n">res</span><span class="p">,</span> <span class="o">*</span><span class="n">rp</span><span class="p">;</span>
|
|
|
|
<span class="n">memset</span><span class="p">(</span><span class="o">&</span><span class="n">hints</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">hints</span><span class="p">));</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_family</span> <span class="o">=</span> <span class="n">AF_UNSPEC</span><span class="p">;</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_socktype</span> <span class="o">=</span> <span class="n">SOCK_STREAM</span><span class="p">;</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_flags</span> <span class="o">=</span> <span class="n">AI_PASSIVE</span><span class="p">;</span>
|
|
<span class="cp">#ifdef AI_ADDRCONFIG</span>
|
|
<span class="n">hints</span><span class="p">.</span><span class="n">ai_flags</span> <span class="o">|=</span> <span class="n">AI_ADDRCONFIG</span><span class="p">;</span>
|
|
<span class="cp">#endif </span><span class="cm">/* AI_ADDRCONFIG */</span><span class="cp"></span>
|
|
|
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">getaddrinfo</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">service</span><span class="p">,</span> <span class="o">&</span><span class="n">hints</span><span class="p">,</span> <span class="o">&</span><span class="n">res</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="nb">NULL</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
<span class="k">for</span> <span class="p">(</span><span class="n">rp</span> <span class="o">=</span> <span class="n">res</span><span class="p">;</span> <span class="n">rp</span><span class="p">;</span> <span class="n">rp</span> <span class="o">=</span> <span class="n">rp</span><span class="o">-></span><span class="n">ai_next</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="k">struct</span> <span class="n">evconnlistener</span> <span class="o">*</span><span class="n">listener</span><span class="p">;</span>
|
|
<span class="n">listener</span> <span class="o">=</span> <span class="n">evconnlistener_new_bind</span><span class="p">(</span>
|
|
<span class="n">evbase</span><span class="p">,</span> <span class="n">acceptcb</span><span class="p">,</span> <span class="n">app_ctx</span><span class="p">,</span> <span class="n">LEV_OPT_CLOSE_ON_FREE</span> <span class="o">|</span> <span class="n">LEV_OPT_REUSEABLE</span><span class="p">,</span>
|
|
<span class="mi">16</span><span class="p">,</span> <span class="n">rp</span><span class="o">-></span><span class="n">ai_addr</span><span class="p">,</span> <span class="n">rp</span><span class="o">-></span><span class="n">ai_addrlen</span><span class="p">);</span>
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="p">)</span> <span class="p">{</span>
|
|
<span class="n">freeaddrinfo</span><span class="p">(</span><span class="n">res</span><span class="p">);</span>
|
|
|
|
<span class="k">return</span><span class="p">;</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 start listener"</span><span class="p">);</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">void</span> <span class="nf">initialize_app_context</span><span class="p">(</span><span class="n">app_context</span> <span class="o">*</span><span class="n">app_ctx</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="p">{</span>
|
|
<span class="n">memset</span><span class="p">(</span><span class="n">app_ctx</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">app_context</span><span class="p">));</span>
|
|
<span class="n">app_ctx</span><span class="o">-></span><span class="n">ssl_ctx</span> <span class="o">=</span> <span class="n">ssl_ctx</span><span class="p">;</span>
|
|
<span class="n">app_ctx</span><span class="o">-></span><span class="n">evbase</span> <span class="o">=</span> <span class="n">evbase</span><span class="p">;</span>
|
|
<span class="p">}</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">service</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">key_file</span><span class="p">,</span>
|
|
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">cert_file</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">app_context</span> <span class="n">app_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">ssl_ctx</span> <span class="o">=</span> <span class="n">create_ssl_ctx</span><span class="p">(</span><span class="n">key_file</span><span class="p">,</span> <span class="n">cert_file</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">initialize_app_context</span><span class="p">(</span><span class="o">&</span><span class="n">app_ctx</span><span class="p">,</span> <span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">evbase</span><span class="p">);</span>
|
|
<span class="n">start_listen</span><span class="p">(</span><span class="n">evbase</span><span class="p">,</span> <span class="n">service</span><span class="p">,</span> <span class="o">&</span><span class="n">app_ctx</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">4</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-server PORT KEY_FILE CERT_FILE</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">OPENSSL_config</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
|
|
<span class="n">OpenSSL_add_all_algorithms</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="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">argv</span><span class="p">[</span><span class="mi">3</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-hpack.html" class="btn btn-neutral float-right" title="Tutorial: HPACK API">Next <span class="fa fa-arrow-circle-right"></span></a>
|
|
|
|
|
|
<a href="tutorial-client.html" class="btn btn-neutral" title="Tutorial: HTTP/2 client"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
|
|
|
</div>
|
|
|
|
|
|
<hr/>
|
|
|
|
<div role="contentinfo">
|
|
<p>
|
|
© Copyright 2012, 2015, Tatsuhiro Tsujikawa.
|
|
</p>
|
|
</div>
|
|
|
|
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
|
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
var DOCUMENTATION_OPTIONS = {
|
|
URL_ROOT:'./',
|
|
VERSION:'0.7.8-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>
|
|
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript" src="_static/js/theme.js"></script>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
jQuery(function () {
|
|
SphinxRtdTheme.StickyNav.enable();
|
|
});
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html> |