<!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 1.38.0 documentation</title> <link rel="stylesheet" href="_static/css/theme.css" type="text/css" /> <link rel="index" title="Index" href="genindex.html"/> <link rel="search" title="Search" href="search.html"/> <link rel="top" title="nghttp2 1.38.0 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="_static/js/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-scroll"> <div class="wy-side-nav-search"> <a href="index.html" class="icon icon-home"> nghttp2 </a> <div class="version"> 1.38.0 </div> <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></li> <li class="toctree-l1"><a class="reference internal" href="contribute.html">Contribution Guidelines</a></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></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></li> <li class="toctree-l1"><a class="reference internal" href="nghttp.1.html">nghttp(1)</a></li> <li class="toctree-l1"><a class="reference internal" href="nghttpd.1.html">nghttpd(1)</a></li> <li class="toctree-l1"><a class="reference internal" href="nghttpx.1.html">nghttpx(1)</a></li> <li class="toctree-l1"><a class="reference internal" href="h2load.1.html">h2load(1)</a></li> <li class="toctree-l1"><a class="reference internal" href="nghttpx-howto.html">nghttpx - HTTP/2 proxy - HOW-TO</a></li> <li class="toctree-l1"><a class="reference internal" href="h2load-howto.html">h2load - HTTP/2 benchmarking tool - HOW-TO</a></li> <li class="toctree-l1"><a class="reference internal" href="programmers-guide.html">Programmers’ Guide</a></li> <li class="toctree-l1"><a class="reference internal" href="apiref.html">API Reference</a></li> <li class="toctree-l1"><a class="reference internal" href="libnghttp2_asio.html">libnghttp2_asio: High level HTTP/2 C++ library</a></li> <li class="toctree-l1"><a class="reference internal" href="python-apiref.html">Python API Reference</a></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/nghttp2/nghttp2">Source</a></li> <li class="toctree-l1"><a class="reference external" href="https://github.com/nghttp2/nghttp2/issues">Issues</a></li> <li class="toctree-l1"><a class="reference external" href="https://nghttp2.org/">nghttp2.org</a></li> </ul> </div> </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" itemscope="itemscope" itemtype="http://schema.org/Article"> <div itemprop="articleBody"> <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 a 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. The source also resides in the examples directory in the archive or repository.</p> <p>This simple server takes 3 arguments: The port number to listen on, the path to your SSL/TLS private key file, and the path to your certificate file. The synopsis is:</p> <div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ 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>The server starts with some libevent and OpenSSL setup in the <code class="docutils literal notranslate"><span class="pre">main()</span></code> and <code class="docutils literal notranslate"><span class="pre">run()</span></code> functions. This setup isn’t specific to nghttp2, but one thing you should look at is setup of the NPN callback. The NPN callback is used by the server to advertise which application protocols the server supports to a client. In this example program, when creating the <code class="docutils literal notranslate"><span class="pre">SSL_CTX</span></code> 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 <code class="docutils literal notranslate"><span class="pre">SSL_CTX</span></code> object in the program’s entire lifetime.</p> <p>If you are following TLS related RFC, you know that NPN is not the standardized way to negotiate HTTP/2. NPN itself is not even published as RFC. The standard way to negotiate HTTP/2 is ALPN, Application-Layer Protocol Negotiation Extension, defined in <a class="reference external" href="https://tools.ietf.org/html/rfc7301">RFC 7301</a>. The one caveat of ALPN is that OpenSSL >= 1.0.2 is required. We use macro to enable/disable ALPN support depending on OpenSSL version. In ALPN, client sends the list of supported application protocols, and server selects one of them. We provide the callback for it:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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="cp">#if OPENSSL_VERSION_NUMBER >= 0x10002000L</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">alpn_select_proto_cb</span><span class="p">(</span><span class="n">SSL</span> <span class="o">*</span><span class="n">ssl</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">out</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">outlen</span><span class="p">,</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">in</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">inlen</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span> <span class="n">_U_</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_select_next_protocol</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">**</span><span class="p">)</span><span class="n">out</span><span class="p">,</span> <span class="n">outlen</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">inlen</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">rv</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">SSL_TLSEXT_ERR_NOACK</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="n">SSL_TLSEXT_ERR_OK</span><span class="p">;</span> <span class="p">}</span> <span class="cp">#endif </span><span class="c1">// OPENSSL_VERSION_NUMBER >= 0x10002000L</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="cp">#if OPENSSL_VERSION_NUMBER >= 0x10002000L</span> <span class="n">SSL_CTX_set_alpn_select_cb</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">alpn_select_proto_cb</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> <span class="cp">#endif </span><span class="c1">// OPENSSL_VERSION_NUMBER >= 0x10002000L</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 strings, with exactly one byte 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, which is exported in the identifier <a class="reference internal" href="macros.html#c.NGHTTP2_PROTO_VERSION_ID" title="NGHTTP2_PROTO_VERSION_ID"><code class="xref c c-macro docutils literal notranslate"><span class="pre">NGHTTP2_PROTO_VERSION_ID</span></code></a>. The <code class="docutils literal notranslate"><span class="pre">next_proto_cb()</span></code> 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 <code class="docutils literal notranslate"><span class="pre">SSL_CTX</span></code> object using <code class="docutils literal notranslate"><span class="pre">SSL_CTX_set_next_protos_advertised_cb()</span></code>.</p> <p>In <code class="docutils literal notranslate"><span class="pre">alpn_select_proto_cb()</span></code>, we use <a class="reference internal" href="nghttp2_select_next_protocol.html#c.nghttp2_select_next_protocol" title="nghttp2_select_next_protocol"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_select_next_protocol()</span></code></a> to select application protocol. The <a class="reference internal" href="nghttp2_select_next_protocol.html#c.nghttp2_select_next_protocol" title="nghttp2_select_next_protocol"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_select_next_protocol()</span></code></a> returns 1 only if it selected h2 (ALPN identifier for HTTP/2), and out parameters were assigned accordingly.</p> <p>Next, let’s take a look at the main structures used by the example application:</p> <p>We use the <code class="docutils literal notranslate"><span class="pre">app_context</span></code> structure to store application-wide data:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>We use the <code class="docutils literal notranslate"><span class="pre">http2_session_data</span></code> structure to store session-level (which corresponds to one HTTP/2 connection) data:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>We use the <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> structure to store stream-level data:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>A single HTTP/2 session can have multiple streams. To manage them, we use a doubly linked list: The first element of this list is pointed to by the <code class="docutils literal notranslate"><span class="pre">root->next</span></code> in <code class="docutils literal notranslate"><span class="pre">http2_session_data</span></code>. Initially, <code class="docutils literal notranslate"><span class="pre">root->next</span></code> is <code class="docutils literal notranslate"><span class="pre">NULL</span></code>.</p> <p>libevent’s bufferevent structure is used to perform network I/O, with the pointer to the bufferevent stored in the <code class="docutils literal notranslate"><span class="pre">http2_session_data</span></code> structure. Note that the bufferevent object is kept in <code class="docutils literal notranslate"><span class="pre">http2_session_data</span></code> and not in <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code>. This is because <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> is just a logical stream multiplexed over the single connection managed by the bufferevent in <code class="docutils literal notranslate"><span class="pre">http2_session_data</span></code>.</p> <p>We first create a listener object to accept incoming connections. libevent’s <code class="docutils literal notranslate"><span class="pre">struct</span> <span class="pre">evconnlistener</span></code> is used for this purpose:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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="p">(</span><span class="kt">int</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 <code class="docutils literal notranslate"><span class="pre">acceptcb</span></code> callback, which is called when a new connection is accepted:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>Here we create the <code class="docutils literal notranslate"><span class="pre">http2_session_data</span></code> object. The connection’s bufferevent is initialized at the same time. We specify three callbacks for the bufferevent: <code class="docutils literal notranslate"><span class="pre">readcb</span></code>, <code class="docutils literal notranslate"><span class="pre">writecb</span></code>, and <code class="docutils literal notranslate"><span class="pre">eventcb</span></code>.</p> <p>The <code class="docutils literal notranslate"><span class="pre">eventcb()</span></code> callback is invoked by the libevent event loop when an event (e.g. connection has been established, timeout, etc.) occurs on the underlying network socket:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">alpn</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">alpnlen</span> <span class="o">=</span> <span class="mi">0</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">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">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">SSL_get0_next_proto_negotiated</span><span class="p">(</span><span class="n">ssl</span><span class="p">,</span> <span class="o">&</span><span class="n">alpn</span><span class="p">,</span> <span class="o">&</span><span class="n">alpnlen</span><span class="p">);</span> <span class="cp">#if OPENSSL_VERSION_NUMBER >= 0x10002000L</span> <span class="k">if</span> <span class="p">(</span><span class="n">alpn</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span> <span class="n">SSL_get0_alpn_selected</span><span class="p">(</span><span class="n">ssl</span><span class="p">,</span> <span class="o">&</span><span class="n">alpn</span><span class="p">,</span> <span class="o">&</span><span class="n">alpnlen</span><span class="p">);</span> <span class="p">}</span> <span class="cp">#endif </span><span class="c1">// OPENSSL_VERSION_NUMBER >= 0x10002000L</span> <span class="k">if</span> <span class="p">(</span><span class="n">alpn</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">||</span> <span class="n">alpnlen</span> <span class="o">!=</span> <span class="mi">2</span> <span class="o">||</span> <span class="n">memcmp</span><span class="p">(</span><span class="s">"h2"</span><span class="p">,</span> <span class="n">alpn</span><span class="p">,</span> <span class="mi">2</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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s h2 is not negotiated</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">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="n">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="o">||</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="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>Here we validate that HTTP/2 is negotiated, and if not, drop connection.</p> <p>For the <code class="docutils literal notranslate"><span class="pre">BEV_EVENT_EOF</span></code>, <code class="docutils literal notranslate"><span class="pre">BEV_EVENT_ERROR</span></code>, and <code class="docutils literal notranslate"><span class="pre">BEV_EVENT_TIMEOUT</span></code> events, we just simply tear down the connection. The <code class="docutils literal notranslate"><span class="pre">delete_http2_session_data()</span></code> function destroys the <code class="docutils literal notranslate"><span class="pre">http2_session_data</span></code> object and its associated bufferevent member. As a result, the underlying connection is closed.</p> <p>The <code class="docutils literal notranslate"><span class="pre">BEV_EVENT_CONNECTED</span></code> event is invoked when SSL/TLS handshake has completed successfully. After this we are ready to begin communicating via HTTP/2.</p> <p>The <code class="docutils literal notranslate"><span class="pre">initialize_nghttp2_session()</span></code> function initializes the nghttp2 session object and several callbacks:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span> <span class="kt">void</span> <span class="nf">initialize_nghttp2_session</span><span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span><span class="p">)</span> <span class="p">{</span> <span class="n">nghttp2_session_callbacks</span> <span class="o">*</span><span class="n">callbacks</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_new</span><span class="p">(</span><span class="o">&</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="n">callbacks</span><span class="p">,</span> <span class="n">session_data</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="p">}</span> </pre></div> </div> <p>Since we are creating a server, we use <a class="reference internal" href="nghttp2_session_server_new.html#c.nghttp2_session_server_new" title="nghttp2_session_server_new"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_server_new()</span></code></a> to initialize the nghttp2 session object. We also setup 5 callbacks for the nghttp2 session, these are explained later.</p> <p>The server now begins by sending the server connection preface, which always consists of a SETTINGS frame. <code class="docutils literal notranslate"><span class="pre">send_server_connection_header()</span></code> configures and submits it:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>In the example SETTINGS frame we’ve set SETTINGS_MAX_CONCURRENT_STREAMS to 100. <a class="reference internal" href="nghttp2_submit_settings.html#c.nghttp2_submit_settings" title="nghttp2_submit_settings"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_submit_settings()</span></code></a> is used to queue the frame for transmission, but note it only queues the frame for transmission, and doesn’t actually send it. All functions in the <code class="docutils literal notranslate"><span class="pre">nghttp2_submit_*()</span></code> family have this property. To actually send the frame, <a class="reference internal" href="nghttp2_session_send.html#c.nghttp2_session_send" title="nghttp2_session_send"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_send()</span></code></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 <code class="docutils literal notranslate"><span class="pre">session_recv()</span></code> function:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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="p">(</span><span class="kt">size_t</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="nghttp2_session_mem_recv.html#c.nghttp2_session_mem_recv" title="nghttp2_session_mem_recv"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_mem_recv()</span></code></a> function. The <a class="reference internal" href="nghttp2_session_mem_recv.html#c.nghttp2_session_mem_recv" title="nghttp2_session_mem_recv"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_mem_recv()</span></code></a> function processes the data and may both invoke the previously setup callbacks and also queue outgoing frames. To send any pending outgoing frames, we immediately call <code class="docutils literal notranslate"><span class="pre">session_send()</span></code>.</p> <p>The <code class="docutils literal notranslate"><span class="pre">session_send()</span></code> function is defined as follows:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>The <a class="reference internal" href="nghttp2_session_send.html#c.nghttp2_session_send" title="nghttp2_session_send"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_send()</span></code></a> function serializes the frame into wire format and calls the <code class="docutils literal notranslate"><span class="pre">send_callback()</span></code>, which is of type <a class="reference internal" href="types.html#c.nghttp2_send_callback" title="nghttp2_send_callback"><code class="xref c c-type docutils literal notranslate"><span class="pre">nghttp2_send_callback</span></code></a>. The <code class="docutils literal notranslate"><span class="pre">send_callback()</span></code> is defined as follows:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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="p">(</span><span class="kt">ssize_t</span><span class="p">)</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="nghttp2_session_send.html#c.nghttp2_session_send" title="nghttp2_session_send"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_send()</span></code></a> continues to write all frames queued so far. If we were writing the data to a non-blocking socket directly using the <code class="docutils literal notranslate"><span class="pre">write()</span></code> system call in the <code class="docutils literal notranslate"><span class="pre">send_callback()</span></code>, we’d soon receive an <code class="docutils literal notranslate"><span class="pre">EAGAIN</span></code> or <code class="docutils literal notranslate"><span class="pre">EWOULDBLOCK</span></code> error since sockets have a limited send buffer. If that happens, it’s possible to return <a class="reference internal" href="enums.html#c.NGHTTP2_ERR_WOULDBLOCK" title="NGHTTP2_ERR_WOULDBLOCK"><code class="xref c c-macro docutils literal notranslate"><span class="pre">NGHTTP2_ERR_WOULDBLOCK</span></code></a> to signal the nghttp2 library to stop sending further data. But here, when writing to the bufferevent, we have to regulate the amount data to 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 <code class="docutils literal notranslate"><span class="pre">OUTPUT_WOULDBLOCK_THRESHOLD</span></code> bytes, we stop writing data and return <a class="reference internal" href="enums.html#c.NGHTTP2_ERR_WOULDBLOCK" title="NGHTTP2_ERR_WOULDBLOCK"><code class="xref c c-macro docutils literal notranslate"><span class="pre">NGHTTP2_ERR_WOULDBLOCK</span></code></a>.</p> <p>The next bufferevent callback is <code class="docutils literal notranslate"><span class="pre">readcb()</span></code>, which is invoked when data is available to read in the bufferevent input buffer:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>In this function, we just call <code class="docutils literal notranslate"><span class="pre">session_recv()</span></code> to process incoming data.</p> <p>The third bufferevent callback is <code class="docutils literal notranslate"><span class="pre">writecb()</span></code>, which is invoked when all data in the bufferevent output buffer has been sent:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </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 can state whether the connection should be dropped or not. More specifically, if both <a class="reference internal" href="nghttp2_session_want_read.html#c.nghttp2_session_want_read" title="nghttp2_session_want_read"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_want_read()</span></code></a> and <a class="reference internal" href="nghttp2_session_want_write.html#c.nghttp2_session_want_write" title="nghttp2_session_want_write"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_want_write()</span></code></a> return 0, the connection is no-longer required and can be closed. Since we are using bufferevent and its deferred callback option, the bufferevent output buffer may still contain pending data when the <code class="docutils literal notranslate"><span class="pre">writecb()</span></code> is called. To handle this, we check whether the output buffer is empty or not. If all of these conditions are met, we drop connection.</p> <p>Otherwise, we call <code class="docutils literal notranslate"><span class="pre">session_send()</span></code> to process the pending output data. Remember that in <code class="docutils literal notranslate"><span class="pre">send_callback()</span></code>, 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 <code class="docutils literal notranslate"><span class="pre">send_callback()</span></code>. Let’s learn about the remaining nghttp2 callbacks setup in <code class="docutils literal notranslate"><span class="pre">initialize_nghttp2_setup()</span></code> function.</p> <p>The <code class="docutils literal notranslate"><span class="pre">on_begin_headers_callback()</span></code> function is invoked when the reception of a header block in HEADERS or PUSH_PROMISE frame is started:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </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 <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> object to store the stream related data. We associate the created <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> object with the stream in the nghttp2 session object using <code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_set_stream_user_data()</span></code>. The <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> object can later be easily retrieved from the stream, 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 <code class="docutils literal notranslate"><span class="pre">on_header_callback</span></code> function, which is called after <code class="docutils literal notranslate"><span class="pre">on_begin_headers_callback()</span></code>:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>We search for the <code class="docutils literal notranslate"><span class="pre">:path</span></code> header field among the request headers and store the requested path in the <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> object. In this example program, we ignore the <code class="docutils literal notranslate"><span class="pre">:method</span></code> header field and always treat the request as a GET request.</p> <p>The <code class="docutils literal notranslate"><span class="pre">on_frame_recv_callback()</span></code> function is invoked when a frame is fully received:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>First we retrieve the <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> object associated with the stream in <code class="docutils literal notranslate"><span class="pre">on_begin_headers_callback()</span></code> using <a class="reference internal" href="nghttp2_session_get_stream_user_data.html#c.nghttp2_session_get_stream_user_data" title="nghttp2_session_get_stream_user_data"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_session_get_stream_user_data()</span></code></a>. If the requested path cannot be served for some reason (e.g. file is not found), we send a 404 response using <code class="docutils literal notranslate"><span class="pre">error_reply()</span></code>. Otherwise, we open the requested file and send its content. We send the header field <code class="docutils literal notranslate"><span class="pre">:status</span></code> as a single response header.</p> <p>Sending the file content is performed by the <code class="docutils literal notranslate"><span class="pre">send_response()</span></code> function:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>nghttp2 uses the <a class="reference internal" href="types.html#c.nghttp2_data_provider" title="nghttp2_data_provider"><code class="xref c c-type docutils literal notranslate"><span class="pre">nghttp2_data_provider</span></code></a> structure to send the entity body to the remote peer. The <code class="docutils literal notranslate"><span class="pre">source</span></code> member of this structure is a union, which can be either a void pointer or an int (which is intended to be used as file descriptor). In this example server, we use it as a file descriptor. We also set the <code class="docutils literal notranslate"><span class="pre">file_read_callback()</span></code> callback function to read the contents of the file:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>If an error occurs while reading the file, we return <a class="reference internal" href="enums.html#c.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE" title="NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE"><code class="xref c c-macro docutils literal notranslate"><span class="pre">NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE</span></code></a>. This tells the library to send RST_STREAM to the stream. When all data has been read, the <a class="reference internal" href="enums.html#c.NGHTTP2_DATA_FLAG_EOF" title="NGHTTP2_DATA_FLAG_EOF"><code class="xref c c-macro docutils literal notranslate"><span class="pre">NGHTTP2_DATA_FLAG_EOF</span></code></a> flag is set to signal nghttp2 that we have finished reading the file.</p> <p>The <a class="reference internal" href="nghttp2_submit_response.html#c.nghttp2_submit_response" title="nghttp2_submit_response"><code class="xref c c-func docutils literal notranslate"><span class="pre">nghttp2_submit_response()</span></code></a> function is used to send the response to the remote peer.</p> <p>The <code class="docutils literal notranslate"><span class="pre">on_stream_close_callback()</span></code> function is invoked when the stream is about to close:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></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> </pre></div> </div> <p>Lastly, we destroy the <code class="docutils literal notranslate"><span class="pre">http2_stream_data</span></code> object in this function, since the stream is about to close and we no longer need the 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 notranslate"><div class="highlight"><pre><span></span><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 __sgi</span> <span class="cp"># define errx(exitcode, format, args...) \</span> <span class="cp"> { \</span> <span class="cp"> warnx(format, ##args); \</span> <span class="cp"> exit(exitcode); \</span> <span class="cp"> }</span> <span class="cp"># define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))</span> <span class="cp"># define warnx(format, args...) fprintf(stderr, format "\n", ##args)</span> <span class="cp">#endif</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</span> <span class="cpf"><sys/types.h></span><span class="cp"></span> <span class="cp">#ifdef HAVE_SYS_SOCKET_H</span> <span class="cp"># include <sys/socket.h></span> <span class="cp">#endif </span><span class="cm">/* HAVE_SYS_SOCKET_H */</span><span class="cp"></span> <span class="cp">#ifdef HAVE_NETDB_H</span> <span class="cp"># include <netdb.h></span> <span class="cp">#endif </span><span class="cm">/* HAVE_NETDB_H */</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><signal.h></span><span class="cp"></span> <span class="cp">#ifdef HAVE_UNISTD_H</span> <span class="cp"># include <unistd.h></span> <span class="cp">#endif </span><span class="cm">/* HAVE_UNISTD_H */</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><sys/stat.h></span><span class="cp"></span> <span class="cp">#ifdef HAVE_FCNTL_H</span> <span class="cp"># include <fcntl.h></span> <span class="cp">#endif </span><span class="cm">/* HAVE_FCNTL_H */</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><ctype.h></span><span class="cp"></span> <span class="cp">#ifdef HAVE_NETINET_IN_H</span> <span class="cp"># include <netinet/in.h></span> <span class="cp">#endif </span><span class="cm">/* HAVE_NETINET_IN_H */</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><netinet/tcp.h></span><span class="cp"></span> <span class="cp">#ifndef __sgi</span> <span class="cp"># include <err.h></span> <span class="cp">#endif</span> <span class="cp">#include</span> <span class="cpf"><string.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><errno.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><openssl/ssl.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><openssl/err.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><openssl/conf.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><event.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><event2/event.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><event2/bufferevent_ssl.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><event2/listener.h></span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf"><nghttp2/nghttp2.h></span><span class="cp"></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="cp">#ifndef OPENSSL_NO_NEXTPROTONEG</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">ssl</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="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">ssl</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">arg</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="cp">#endif </span><span class="cm">/* !OPENSSL_NO_NEXTPROTONEG */</span><span class="cp"></span> <span class="cp">#if OPENSSL_VERSION_NUMBER >= 0x10002000L</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">alpn_select_proto_cb</span><span class="p">(</span><span class="n">SSL</span> <span class="o">*</span><span class="n">ssl</span><span class="p">,</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">**</span><span class="n">out</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">outlen</span><span class="p">,</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">in</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">inlen</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">rv</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">ssl</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">arg</span><span class="p">;</span> <span class="n">rv</span> <span class="o">=</span> <span class="n">nghttp2_select_next_protocol</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">**</span><span class="p">)</span><span class="n">out</span><span class="p">,</span> <span class="n">outlen</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">inlen</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">rv</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">SSL_TLSEXT_ERR_NOACK</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="n">SSL_TLSEXT_ERR_OK</span><span class="p">;</span> <span class="p">}</span> <span class="cp">#endif </span><span class="cm">/* OPENSSL_VERSION_NUMBER >= 0x10002000L */</span><span class="cp"></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="cp">#ifndef OPENSSL_NO_NEXTPROTONEG</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="cp">#endif </span><span class="cm">/* !OPENSSL_NO_NEXTPROTONEG */</span><span class="cp"></span> <span class="cp">#if OPENSSL_VERSION_NUMBER >= 0x10002000L</span> <span class="n">SSL_CTX_set_alpn_select_cb</span><span class="p">(</span><span class="n">ssl_ctx</span><span class="p">,</span> <span class="n">alpn_select_proto_cb</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> <span class="cp">#endif </span><span class="cm">/* OPENSSL_VERSION_NUMBER >= 0x10002000L */</span><span class="cp"></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="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="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">session_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="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">bufferevent_enable</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">EV_READ</span> <span class="o">|</span> <span class="n">EV_WRITE</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="p">(</span><span class="kt">socklen_t</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="p">(</span><span class="kt">size_t</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="p">,</span> <span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">int</span> <span class="n">flags</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">user_data</span><span class="p">)</span> <span class="p">{</span> <span class="n">http2_session_data</span> <span class="o">*</span><span class="n">session_data</span> <span class="o">=</span> <span class="p">(</span><span class="n">http2_session_data</span> <span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span> <span class="k">struct</span> <span class="n">bufferevent</span> <span class="o">*</span><span class="n">bev</span> <span class="o">=</span> <span class="n">session_data</span><span class="o">-></span><span class="n">bev</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">session</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">flags</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="p">(</span><span class="kt">ssize_t</span><span class="p">)</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="p">(</span><span class="kt">uint8_t</span><span class="p">)(</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="p">(</span><span class="kt">uint8_t</span><span class="p">)(</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="p">(</span><span class="kt">uint8_t</span><span class="p">)(</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="p">(</span><span class="kt">char</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="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="kt">char</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="p">,</span> <span class="kt">int32_t</span> <span class="n">stream_id</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="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="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">session</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">stream_id</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">user_data</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">static</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="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_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="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">flags</span><span class="p">;</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span> <span class="k">switch</span> <span class="p">(</span><span class="n">frame</span><span class="o">-></span><span class="n">hd</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="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="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="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">error_code</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_session_callbacks</span> <span class="o">*</span><span class="n">callbacks</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_new</span><span class="p">(</span><span class="o">&</span><span class="n">session_data</span><span class="o">-></span><span class="n">session</span><span class="p">,</span> <span class="n">callbacks</span><span class="p">,</span> <span class="n">session_data</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="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="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="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">bev</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="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="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">alpn</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">alpnlen</span> <span class="o">=</span> <span class="mi">0</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="p">(</span><span class="kt">void</span><span class="p">)</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 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">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="cp">#ifndef OPENSSL_NO_NEXTPROTONEG</span> <span class="n">SSL_get0_next_proto_negotiated</span><span class="p">(</span><span class="n">ssl</span><span class="p">,</span> <span class="o">&</span><span class="n">alpn</span><span class="p">,</span> <span class="o">&</span><span class="n">alpnlen</span><span class="p">);</span> <span class="cp">#endif </span><span class="cm">/* !OPENSSL_NO_NEXTPROTONEG */</span><span class="cp"></span> <span class="cp">#if OPENSSL_VERSION_NUMBER >= 0x10002000L</span> <span class="k">if</span> <span class="p">(</span><span class="n">alpn</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span> <span class="n">SSL_get0_alpn_selected</span><span class="p">(</span><span class="n">ssl</span><span class="p">,</span> <span class="o">&</span><span class="n">alpn</span><span class="p">,</span> <span class="o">&</span><span class="n">alpnlen</span><span class="p">);</span> <span class="p">}</span> <span class="cp">#endif </span><span class="cm">/* OPENSSL_VERSION_NUMBER >= 0x10002000L */</span><span class="cp"></span> <span class="k">if</span> <span class="p">(</span><span class="n">alpn</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">||</span> <span class="n">alpnlen</span> <span class="o">!=</span> <span class="mi">2</span> <span class="o">||</span> <span class="n">memcmp</span><span class="p">(</span><span class="s">"h2"</span><span class="p">,</span> <span class="n">alpn</span><span class="p">,</span> <span class="mi">2</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">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s h2 is not negotiated</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">delete_http2_session_data</span><span class="p">(</span><span class="n">session_data</span><span class="p">);</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="n">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="o">||</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="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="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="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">listener</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="s">"Could not resolve server address"</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="p">(</span><span class="kt">int</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">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> </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" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a> <a href="tutorial-client.html" class="btn btn-neutral" title="Tutorial: HTTP/2 client" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a> </div> <hr/> <div role="contentinfo"> <p> © Copyright 2012, 2015, 2016, 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:'1.38.0', 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/language_data.js"></script> <script type="text/javascript" src="_static/js/theme.js"></script> <script type="text/javascript"> jQuery(function () { SphinxRtdTheme.StickyNav.enable(); }); </script> </body> </html>