]> granicus.if.org Git - apache/commitdiff
Fold in git archive master of mod_h2 (latest commit 11905f474e)
authorJim Jagielski <jim@apache.org>
Tue, 30 Jun 2015 15:26:16 +0000 (15:26 +0000)
committerJim Jagielski <jim@apache.org>
Tue, 30 Jun 2015 15:26:16 +0000 (15:26 +0000)
from https://github.com/icing/mod_h2 as per software grant.

Since this is a git archive of master (for tracking and IP
provenance history), it includes files that will likely
be removed/renamed/etc...

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1688474 13f79535-47bb-0310-9956-ffa450edef68

350 files changed:
modules/http2/.gitignore [new file with mode: 0644]
modules/http2/AUTHORS [new file with mode: 0644]
modules/http2/COPYING [new file with mode: 0644]
modules/http2/ChangeLog [new file with mode: 0644]
modules/http2/DISCUSS [new file with mode: 0644]
modules/http2/INSTALL [new file with mode: 0644]
modules/http2/LICENSE [new file with mode: 0644]
modules/http2/Makefile.am [new file with mode: 0644]
modules/http2/NEWS [new file with mode: 0644]
modules/http2/README [new file with mode: 0644]
modules/http2/README.md [new file with mode: 0644]
modules/http2/configure.ac [new file with mode: 0644]
modules/http2/m4/ax_check_compile_flag.m4 [new file with mode: 0644]
modules/http2/m4/h2.m4 [new file with mode: 0644]
modules/http2/mod-h2.xcodeproj/project.pbxproj [new file with mode: 0644]
modules/http2/mod-h2.xcodeproj/project.xcworkspace/contents.xcworkspacedata [new file with mode: 0644]
modules/http2/mod-h2.xcodeproj/project.xcworkspace/xcshareddata/mod-h2.xccheckout [new file with mode: 0644]
modules/http2/mod-h2.xcodeproj/xcuserdata/sei.xcuserdatad/xcschemes/mod_h2 make.xcscheme [new file with mode: 0644]
modules/http2/mod-h2.xcodeproj/xcuserdata/sei.xcuserdatad/xcschemes/xcschememanagement.plist [new file with mode: 0644]
modules/http2/mod_h2/.gitignore [new file with mode: 0644]
modules/http2/mod_h2/Makefile.am [new file with mode: 0644]
modules/http2/mod_h2/h2_alpn.c [new file with mode: 0644]
modules/http2/mod_h2/h2_alpn.h [new file with mode: 0644]
modules/http2/mod_h2/h2_alt_svc.c [new file with mode: 0644]
modules/http2/mod_h2/h2_alt_svc.h [new file with mode: 0644]
modules/http2/mod_h2/h2_config.c [new file with mode: 0644]
modules/http2/mod_h2/h2_config.h [new file with mode: 0644]
modules/http2/mod_h2/h2_conn.c [new file with mode: 0644]
modules/http2/mod_h2/h2_conn.h [new file with mode: 0644]
modules/http2/mod_h2/h2_conn_io.c [new file with mode: 0644]
modules/http2/mod_h2/h2_conn_io.h [new file with mode: 0644]
modules/http2/mod_h2/h2_ctx.c [new file with mode: 0644]
modules/http2/mod_h2/h2_ctx.h [new file with mode: 0644]
modules/http2/mod_h2/h2_from_h1.c [new file with mode: 0644]
modules/http2/mod_h2/h2_from_h1.h [new file with mode: 0644]
modules/http2/mod_h2/h2_h2.c [new file with mode: 0644]
modules/http2/mod_h2/h2_h2.h [new file with mode: 0644]
modules/http2/mod_h2/h2_io.c [new file with mode: 0644]
modules/http2/mod_h2/h2_io.h [new file with mode: 0644]
modules/http2/mod_h2/h2_io_set.c [new file with mode: 0644]
modules/http2/mod_h2/h2_io_set.h [new file with mode: 0644]
modules/http2/mod_h2/h2_mplx.c [new file with mode: 0644]
modules/http2/mod_h2/h2_mplx.h [new file with mode: 0644]
modules/http2/mod_h2/h2_private.h [new file with mode: 0644]
modules/http2/mod_h2/h2_request.c [new file with mode: 0644]
modules/http2/mod_h2/h2_request.h [new file with mode: 0644]
modules/http2/mod_h2/h2_response.c [new file with mode: 0644]
modules/http2/mod_h2/h2_response.h [new file with mode: 0644]
modules/http2/mod_h2/h2_session.c [new file with mode: 0644]
modules/http2/mod_h2/h2_session.h [new file with mode: 0644]
modules/http2/mod_h2/h2_stream.c [new file with mode: 0644]
modules/http2/mod_h2/h2_stream.h [new file with mode: 0644]
modules/http2/mod_h2/h2_stream_set.c [new file with mode: 0644]
modules/http2/mod_h2/h2_stream_set.h [new file with mode: 0644]
modules/http2/mod_h2/h2_task.c [new file with mode: 0644]
modules/http2/mod_h2/h2_task.h [new file with mode: 0644]
modules/http2/mod_h2/h2_task_input.c [new file with mode: 0644]
modules/http2/mod_h2/h2_task_input.h [new file with mode: 0644]
modules/http2/mod_h2/h2_task_output.c [new file with mode: 0644]
modules/http2/mod_h2/h2_task_output.h [new file with mode: 0644]
modules/http2/mod_h2/h2_task_queue.c [new file with mode: 0644]
modules/http2/mod_h2/h2_task_queue.h [new file with mode: 0644]
modules/http2/mod_h2/h2_to_h1.c [new file with mode: 0644]
modules/http2/mod_h2/h2_to_h1.h [new file with mode: 0644]
modules/http2/mod_h2/h2_upgrade.c [new file with mode: 0644]
modules/http2/mod_h2/h2_upgrade.h [new file with mode: 0644]
modules/http2/mod_h2/h2_util.c [new file with mode: 0644]
modules/http2/mod_h2/h2_util.h [new file with mode: 0644]
modules/http2/mod_h2/h2_version.h.in [new file with mode: 0644]
modules/http2/mod_h2/h2_worker.c [new file with mode: 0644]
modules/http2/mod_h2/h2_worker.h [new file with mode: 0644]
modules/http2/mod_h2/h2_workers.c [new file with mode: 0644]
modules/http2/mod_h2/h2_workers.h [new file with mode: 0644]
modules/http2/mod_h2/m4/h2.m4 [new file with mode: 0644]
modules/http2/mod_h2/mod_h2.c [new file with mode: 0644]
modules/http2/mod_h2/mod_h2.h [new file with mode: 0644]
modules/http2/sandbox/.gitignore [new file with mode: 0644]
modules/http2/sandbox/Makefile.am [new file with mode: 0644]
modules/http2/sandbox/httpd/.gitignore [new file with mode: 0644]
modules/http2/sandbox/httpd/Makefile [new file with mode: 0644]
modules/http2/sandbox/httpd/get-openssl-latest.sh [new file with mode: 0755]
modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile.in [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/NWGNUmakefile [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/README [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/README.dsov.fig [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/README.dsov.ps [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/config.m4 [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.dsp [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.h [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/modules.mk [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_config.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_init.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_io.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_kernel.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_log.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_mutex.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_ocsp.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_pphrase.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_rand.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_vars.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_private.h [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_scache.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ocsp.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ssl.c [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ssl.h [new file with mode: 0644]
modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_stapling.c [new file with mode: 0644]
modules/http2/sandbox/httpd/packages/pcre-8.36.tar.gz [new file with mode: 0644]
modules/http2/sandbox/httpd/patches/httpd-2.4.12-alpn-v5.patch [new file with mode: 0644]
modules/http2/sandbox/httpd/patches/httpd-2.4.x-alpn-v4.patch [new file with mode: 0644]
modules/http2/sandbox/httpd/patches/httpd-alpn-v4-v5.patch [new file with mode: 0644]
modules/http2/sandbox/httpd/patches/httpd-npn.unified.diff.patch [new file with mode: 0644]
modules/http2/sandbox/httpd/patches/openssl-1.0.2-alpn.patch [new file with mode: 0644]
modules/http2/sandbox/httpd/patches/sni_misdirect.patch [new file with mode: 0644]
modules/http2/sandbox/nghttp2/Makefile [new file with mode: 0644]
modules/http2/sandbox/test/Makefile [new file with mode: 0644]
modules/http2/sandbox/test/bin/php-wrapper [new file with mode: 0644]
modules/http2/sandbox/test/bin/testrun [new file with mode: 0644]
modules/http2/sandbox/test/clients/Makefile [new file with mode: 0644]
modules/http2/sandbox/test/conf/httpd.conf [new file with mode: 0644]
modules/http2/sandbox/test/conf/mods-available/mpm_event.load [new file with mode: 0644]
modules/http2/sandbox/test/conf/mods-available/mpm_prefork.load [new file with mode: 0644]
modules/http2/sandbox/test/conf/mods-available/mpm_worker.load [new file with mode: 0644]
modules/http2/sandbox/test/conf/modules.conf [new file with mode: 0644]
modules/http2/sandbox/test/conf/sites/aaa-noh2.example.org.conf [new file with mode: 0644]
modules/http2/sandbox/test/conf/sites/test-ser.example.org.conf [new file with mode: 0644]
modules/http2/sandbox/test/conf/sites/test.example.org.conf [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/.gitignore [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/ca.pem [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/cacerts.pem [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/extensions.conf [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/mod-h2.greenbytes.de.pem [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/noh2.example.org.x509.input [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/test-ser.example.org.x509.input [new file with mode: 0644]
modules/http2/sandbox/test/conf/ssl/test.example.org.x509.input [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/001.html [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/002.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/003.html [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/003/003_img.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004.html [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_002.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_003.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_004.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_005.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_006.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_007.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_008.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_009.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_010.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_011.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_012.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_013.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_014.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_015.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_016.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_017.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_018.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_019.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_020.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_021.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_022.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_023.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_024.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_025.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_026.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_027.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_028.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_029.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_030.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_031.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_032.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_033.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_034.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_035.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_036.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_037.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_038.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_039.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_040.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_041.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_042.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_043.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_044.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_045.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_046.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_047.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_048.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_049.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_050.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_051.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_052.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_053.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_054.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_055.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_056.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_057.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_058.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_059.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_060.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_061.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_062.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_063.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_064.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_065.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_066.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_067.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_068.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_069.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_070.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_071.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_072.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_073.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_074.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_075.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_076.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_077.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_078.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_079.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_080.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_081.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_082.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_083.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_084.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_085.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_086.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_087.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_088.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_089.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_090.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_091.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_092.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_093.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_094.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_095.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_096.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_097.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_098.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_099.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_100.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_101.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_102.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_103.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_104.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_105.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_106.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_107.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_108.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_109.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_110.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_111.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_112.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_113.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_114.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_115.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_116.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_117.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_118.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_119.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_120.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_121.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_122.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_123.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_124.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_125.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_126.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_127.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_128.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_129.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_130.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_131.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_132.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_133.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_134.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_135.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_136.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_137.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_138.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_139.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_140.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_141.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_142.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_143.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_144.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_145.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_146.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_147.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_148.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_149.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_150.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_151.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_152.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_153.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_154.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_155.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_156.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_157.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_158.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_159.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_160.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_161.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_162.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_163.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_164.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_165.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_166.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_167.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_168.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_169.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_170.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_171.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_172.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_173.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_174.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_175.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_176.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_177.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_178.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_179.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_180.jpg [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/005.txt [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/006.html [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/006/006.css [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/006/006.js [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/007.html [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/007/007.py [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/009.py [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/files/empty.txt [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/funky.png [new file with mode: 0755]
modules/http2/sandbox/test/htdocs/test.example.org/hello.py [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/index.html [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/info.php [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/necho.py [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/sei.png [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/upload.py [new file with mode: 0644]
modules/http2/sandbox/test/htdocs/test.example.org/xxx-1.0.2a.tar.gz [new file with mode: 0644]
modules/http2/sandbox/test/load-urls-1.txt [new file with mode: 0644]
modules/http2/sandbox/test/test_alt_host.sh [new file with mode: 0644]
modules/http2/sandbox/test/test_common.sh [new file with mode: 0644]
modules/http2/sandbox/test/test_curl_altsvc.sh [new file with mode: 0644]
modules/http2/sandbox/test/test_curl_get.sh [new file with mode: 0644]
modules/http2/sandbox/test/test_curl_post.sh [new file with mode: 0644]
modules/http2/sandbox/test/test_nghttp_get.sh [new file with mode: 0644]
modules/http2/sandbox/test/test_nghttp_post.sh [new file with mode: 0644]
modules/http2/setup/Makefile.am [new file with mode: 0644]
modules/http2/setup/h2.conf [new file with mode: 0644]
modules/http2/setup/h2.load [new file with mode: 0644]
modules/http2/setup/install-config.sh [new file with mode: 0644]

diff --git a/modules/http2/.gitignore b/modules/http2/.gitignore
new file mode 100644 (file)
index 0000000..ca49620
--- /dev/null
@@ -0,0 +1,35 @@
+*.xcuserstate
+sandbox/httpd/packages/httpd-2.4.x.tar.gz
+sandbox/test/conf/sites/mod-h2.greenbytes.de.conf
+*.o
+*.slo
+*.lo
+*.la
+*.pcap
+.libs
+.configured
+.deps
+compile
+aclocal.m4
+autom4te.cache
+autoscan.log
+config.guess
+config.log
+config.status
+config.sub
+config.h
+config.h.in
+config.h.in~
+configure
+configure.scan
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+Makefile.in
+Makefile
+mod_h2-*.tar.gz
+mod_h2/h2_version.h
+m4
diff --git a/modules/http2/AUTHORS b/modules/http2/AUTHORS
new file mode 100644 (file)
index 0000000..07814ee
--- /dev/null
@@ -0,0 +1 @@
+Stefan Eissing <stefan.eissing at greenbytes.de>
\ No newline at end of file
diff --git a/modules/http2/COPYING b/modules/http2/COPYING
new file mode 100644 (file)
index 0000000..cde8289
--- /dev/null
@@ -0,0 +1,2 @@
+
+  Please see the file called LICENSE.
diff --git a/modules/http2/ChangeLog b/modules/http2/ChangeLog
new file mode 100644 (file)
index 0000000..e7b03ed
--- /dev/null
@@ -0,0 +1,253 @@
+v0.7.3
+--------------------------------------------------------------------------------
+ * sandbox update to nghttp2 1.0.4
+ * rework of stream scheduling and thread sync
+ * test suite runs with mpm_prefork on OS X and Ubuntu (feedback welcome)
+
+v0.7.2
+--------------------------------------------------------------------------------
+ * fixes crash with certain modules that (correctly) expected their 
+   pre_connection hooks to work. Solves #28
+ * fixes log statements and Makefile to compile sandbox on 32bit linux
+
+v0.7.1
+--------------------------------------------------------------------------------
+ * h2_mplx now uses reference counting to enable a controlled destruction on
+   session termination. If this fixes all the crashes reported, remains to
+   be seen.
+
+v0.7.0
+--------------------------------------------------------------------------------
+ * switching to nghttp2 v1.0.*, compilation + linkage with nghttp2 v0.7.* will
+   not work any more as there have been changes in nghttp2's API. The sandbox
+   builds against v1.0.2 and also pulls curl 7.43.0 which did the same version
+   switch.
+ * keeping support for h2-16/h2-14 ALPN identifiers until a 1.0 release
+ * varying h2 support (on|off) among virtual hosts on the same port is now
+   possible and should work correctly.
+ * improved mod_ssl alpn patch to correctly select the proper virtual host
+   even if the SSL library invokes SNI after ALPN callbacks.
+ * Disabled NPN support in sandbox built. The patch for httpd 2.4.x is still
+   there, but no longer applied.
+ * KNOWN ISSUE: when using mpm_event, the error 
+     "error scoreboard is full, not at MaxRequestWorkers" 
+   sometimes happens. Under investigation. mpm_worker ist stable and recommended
+   for now.
+
+
+v0.6.6
+--------------------------------------------------------------------------------
+ * adding sni_misdirect.patch for httpd/mod_ssl that changes status code when 
+   TLS connections are reused for other vhosts. 
+
+v0.6.5
+--------------------------------------------------------------------------------
+ * httpd scoreboard fixups: scoreboard handles are still updated on the main
+   connection from all stream request incoming from it, since it is not trivial
+   to get new handles for h2 workers. Updating of scoreboard status has been 
+   improved, no workers remain on "G"raceful shutdown in load tests any longer.
+ * some refacoring to isolate tls/alpn/upgrade code
+ * adding php-wrapper script to distribution, was missing
+
+v0.6.4
+--------------------------------------------------------------------------------
+ * accepting "h2" on Upgrade: headers and allowing also Upgrade to work on TLS
+   connections that talk HTTP/1.1.
+ * logging WARNING once when mod_h2 is used in a "prefork" mpm configuration.
+   There seem to be issues with mod_proxy/rewrite and dankging I/O.
+ * added test suites for combinations with mod_proxy and mod_rewrite
+ * added test for request with Host: header where h2 is not enabled
+ * sandbox now builds local openssl 1.0.2 if 1.0.1 or older are installed
+   on the system. Prior 1.0.1 was accepted, but NPN support will be disabled
+   soon.
+ * added "Known Problems" section in README
+ * direct mode should also work with TLS connections, untested currently.
+
+v0.6.3
+--------------------------------------------------------------------------------
+ * h2c direct mode is now enabled on http: virtual hosts. It can be disabled
+   by configuring "H2Direct off" for the base server. This works for clients
+   with prior knowledge that a http server supports h2c.
+
+v0.6.2
+--------------------------------------------------------------------------------
+ * added "H2Direct (on|off)" as config directive. On a non-TLS host, it enables
+   direct h2c communication without any Upgrade dance. Useful for testing, for
+   example with h2load.
+ * fixed concurrency issue observed on a mpm_event server when closing sessions
+
+v0.6.1
+--------------------------------------------------------------------------------
+ * relaxing task finished sync due to segfaults with mpm_event
+ * mod_reqtimeout enabled on stream processing again
+ * merged pr from ecovener, fix for ap_update_vhost_from_headers use
+ * tinkering with buffering on main connection
+
+v0.6.0
+--------------------------------------------------------------------------------
+ * fiddling around with task/worker assignments to use the global h2_workers
+   lock less, giving better parallelism
+ * ./configure --enable-werror enables all sorts of compiler diagnostics, if
+   the chosen compiler supports them
+ * merged pull request with compiler warning fixes by LPardue and @samhurst 
+   (Thanks!)
+ * new config option "H2SerializeHeaders (On|Off)" that determines if request
+   response headers should be serialized/parsed when converting from HTTP/2 to
+   httpd's internal HTTP/1 processing or request_recs are manipulated directly.
+   "Off" is default, "On" gives better compatibility.
+ * new config options "H2HackMpmEvent (On|Off) that enables a hack to make
+   internal connection work with the mpm_event module. Has no effect if other
+   mpm modules have been configured. Defaults to "On"."
+ * upgrading sandbox to nghttp2 0.7.15
+
+v0.5.6
+--------------------------------------------------------------------------------
+ * making SSL variables available in subprocess (e.g. CGI) environments if
+configured (fixes #19)
+
+v0.5.5
+--------------------------------------------------------------------------------
+ * improved transfer of large resources by 50% by more efficient writes
+
+v0.5.4
+ --------------------------------------------------------------------------------
+ * moving request handling and http/1 emulation into worker thread, offloading 
+   main
+ * some fixes in connection shutdown re race condition with still active workers
+ * removing reqtimeout filters on stream connections
+ * taking in latest alpn changes from httpd trunk, made separate npn patch for
+   those who need to run with openssl 1.0.1
+ * fixed handling of transient bucket in stream output data (led to corrupted
+   responses)
+ * reworked task join to eliminate race conditions during session shutdown
+ * info logging is less verbose
+
+v0.5.2
+--------------------------------------------------------------------------------
+ * rewrote the input handling
+    * forwarding headers in camel case to HTTP/1 request prossing
+    * merging duplicate headers into single line with proper separators
+    * eliminated h2_bucket by using apr_bucket_brigades everywhere
+    * added test cases and fixed chunked input processing
+ * sandbox now uses curl 7.42.0
+ * enhanced alpn patch slightly, compatible to old one, no need to repatch mod_ssl on existing installations
+ * adding some tests with fcgi, for sandbox testing please install php5-cgi
+
+v0.5.1
+--------------------------------------------------------------------------------
+ * ensuring HOST header is properly set for internal request handling
+ * updated sandbox to latest nghttp2 release
+
+v0.5.0
+--------------------------------------------------------------------------------
+ * improved resource handling and performance
+ * fixes lookup of correct server config when several virtual hosts are 
+   available
+
+v0.4.5
+--------------------------------------------------------------------------------
+ * fixed base64 decoding to use correct dialect when upgrading to h2c
+
+v0.4.4
+--------------------------------------------------------------------------------
+ * adds missing file in distribution tar, fixes #11
+ * sandbox now uses nghttp2 0.7.11
+
+v0.4.3
+--------------------------------------------------------------------------------
+ * heavy work on internal data transfers, use of apache bucket brigades for
+   improved performance and parallelism
+ * mod_h2 now performs proper flow-control on input. Uploads can no longer
+   flood memory of the server.
+ * changed mod_ssl patch to reflect current trunk changes in regard to NPN/ALPN
+   support.
+ * patched the trunk ALPN patch to make it do the right thing if no 
+   ALPNPreference is configured
+ * added DESTDIR support in Makefile as supplied by Hanno Böck.
+
+v0.4.2
+--------------------------------------------------------------------------------
+ * adding more low-level logging to NPN/ALPN negotiation
+ * added '-Werror' to module compiler flags (thanks @devurandom)
+ * debugged and tcpdumped around h2c startup handling with curl on certain
+   platforms. If you experience connection hangers, report pls with 'trace2'
+   level error log.
+
+v0.4.1
+--------------------------------------------------------------------------------
+ * fixing infinite loop when aborting session with tasks hanging in read
+
+v0.4.0
+--------------------------------------------------------------------------------
+ * h2 + h2-16 will be negiotiated/upgraded. Priority handling is implemented
+   in nghttp2, however assigning requests to workers has currently not the
+   necessary information.
+ * Alt-Svc support based on https://http2.github.io/http2-spec/alt-svc.html#indicator
+   Configuration directives "H2AltSvc" and "H2AltSvcMaxAge" added. Configurable
+   per virtual host.
+ * reduced lock contention and eliminated broadcast signalling on internal 
+   condition variables, improved multi-thread performance
+
+v0.3.1
+--------------------------------------------------------------------------------
+ * defining _GNU_SOURCE to pull in proper features and make APR header files
+   happy, fixes #7 
+
+v0.3.0
+--------------------------------------------------------------------------------
+ * new directory structure, all sandbox related parts have been moved to 
+   own sandbox dir
+ * non-sandbox configuration no longer triggers building of sandbox stuff
+ * cleanup up distribution builds to have smaller and working tar balls
+
+v0.2.2
+--------------------------------------------------------------------------------
+ * optimizations on response reads
+ * being more apacheeeish, using APR_RINGs for queue handling
+ * using openssl latest.tar.gz to retrieve latest openssl for sandbox
+ * fixed crash in large request header handling
+ * better flushing of stream io resulting in performance improvements
+ * added sandbox tests for chunked responses of various sizes
+ * integrated fix (https://github.com/icing/mod_h2/pull/5) from 
+   Jonathan (invader444) to fix chunked encoding when converting http/1
+   responses to http2 unchunked data.
+
+v0.2.1
+--------------------------------------------------------------------------------
+ * changed apr_pool usage to have more recycling, 50% performance increase in 
+   loadtest
+ * added hack to enable mod_h2 to run in mpm_event (experimental)
+ * changed internal stream/data lookup from O(n/2) to O(log(n)) time
+ * fixed index in http2 to http1 header conversion leading to segfaults
+ * update openssl version downloaded for sandbox
+ * using sha256 for self-signed sandbox certificates
+ * sandbox certificate no longer announces itself as roo
+   (last 3 thanks to michael.koeller at greenbytes.de)
+
+v0.2.0
+--------------------------------------------------------------------------------
+ * merged pull request from Tatsuhiro Tsujikawa, removing code for CONTINUATION
+   frames that are never seen by mod_h2
+ * request/response headers are filtered when converting between HTTP/1.1 and
+   HTTP/2 where they can only do harm (Expect/Connection/etc.)
+ * added test cases for uploads
+ * fixed handling of uploads ;-)
+ * enabled h2c upgrade for "OPTIONS *" requests
+ * changed h2_stream_set implementation to use sorted apr array
+ * switched to nghttp2 v0.7.7 in sandbox
+
+v0.1.1
+--------------------------------------------------------------------------------
+ * added cgi test cases (needs python installed)
+ * fixed typo in configure.ac that did not set the CPP flags correctly (#3)
+ * fixed handling of chunked encoding in responses
+ * merged header macro pull request from MATSUMOTO, Ryosuke
+
+v0.1.0
+--------------------------------------------------------------------------------
+ * first alpha release, see README on how to use it and what is all missing
+ * h2 supported with openssl > 1.0.1 and patched mod_ssl (patch included and used in sandbox)
+ * h2c supported
+ * only usable in mpm_worker right now
+ * configure --enable-sandbox for a local install of everything needed
+ * tested with httpd 2.4.12 and nghttp2 0.7.5 on Ubuntu 14.04 and OS X 10.10
diff --git a/modules/http2/DISCUSS b/modules/http2/DISCUSS
new file mode 100644 (file)
index 0000000..c768252
--- /dev/null
@@ -0,0 +1,149 @@
+
+mod_h2 - a http/2 modules for apache httpd
+==========================================
+The mod_h2 Apache httpd module implements the HTTP2 protocol (h2+h2c) on
+top of libnghttp2 for httpd 2.4 servers. For a general description, see
+the README, installation issues are detailed in INSTALL.
+
+This document is for discussion of the module's internals, current issues
+and exploration of ideas.
+
+
+THE GOALS
+---------
+The goals of this module can be stated as follows:
+- make the full htpp2 standard available in Apache httpd
+- provide it as a module on top of httpd 2.4.x
+- support deployment on standard unix platforms
+
+
+HOW IT WORKS
+------------
+The architecture of this module is heavily inspired by Google's mod_spdy:
+The incoming, parallel requests (http2 streams) are dispatched to a thread
+pool, responses are collected and multiplexed on the original connection.
+
+The major players in ascii art:
+
+  h2_conn -> h2_session ------> h2_mplx ----> h2_task / h2_worker
+  (socket)   (nghttp2_session)            |-> h2_task / h2_worker
+                                          |-> h2_task / h2_worker
+                                                ...
+
+h2_session: by using nghttp2 API, is doing the http2 frame work, stream
+            states, flow control, etc. Sits as connection level filter
+            on standard httpd connections. Gets active either by ALPN
+            selection or as HTTP/1.1 Upgrade from a request.
+
+h2_mplx:    is a somewhat specialized bucket_brigate. It multiplexes data
+            buckets associated with stream IDs in both directions and
+            has some specials to reset streams or announce response headers.
+            It also performs flow control on the downlink of streams.
+
+h2_task:    having own conn_rec instance, plus in/out filters at connection
+            level, is converting http2 streams into http/1.1 requests and
+            parses back responses to http2 usable headers and data.
+
+
+LIFETIMES
+---------
+For each connection that uses HTTP/2, a new h2_session is created. That lives
+as long as all objects it creates: h2_stream and h2_task instances. So, when
+closing a h2_session, this waits until all associated h2_streams have
+been destroyed. h2_streams will only be destroyed when their h2_task is either
+removed from the schedule queue or has terminated.
+
+Insofar, the lifetimes from h2_session/h2_stream have the similar relations as
+conn_rec/request_rec with the exception that there can be many simultaneous
+h2_streams active per h2_session (and in various worker threads).
+
+
+THREAD HANDLING
+---------------
+h2_session is only ever accessed from the thread handling the original
+connection. Same for h2_stream instances. The execution of what is necessary
+for execution of a h2_stream happens in h2_task. h2_task gets instantiated
+in the connection thread, but is the handed to a worker and, apart from
+checking its status atomically, not called by any other thread.
+
+The object that shovels the data packages back and forth and is accessed from
+multiple threads is h2_mplx. h2_tasks use it to retrieve their input and feed
+it their output. h2_mplx blocks h2_tasks when no input is available or
+the amount of output queue has reached a certain maximum.
+
+There is a nice thread pool in apr-util which even suports priority scheduling.
+It would be good to exchange the h2_worker(s) for this pool, except mod_h2
+has the use case that streams can be aborted by the client and the
+corresponding task needs to be removed from the schedule without any
+blocking wait for a possibly running task to complete. This is not offered
+by apr-util.
+
+
+LOCKS / CONDITIONS
+------------------
+apr_thread_mutex_t is used for locking. apr_thread_cond_t is used for blocking
+and signalling. The number of such objects created grows linear with the 
+number of parallel main connections, plus the number of worker threads.
+
+This means it is not influenced by the number of outstanding requests. The 
+intention for this is to allow, possibly, many outstanding requests per HTTP/2 
+connection without consuming unnecessary server resources.
+
+
+MEMORY HANDLING
+---------------
+The session pool is a sub pool of the main connection pool, with the twist
+that it has its own allocator (apr_allocator_t). That allocator is protected
+with a apr_thread_mutex (one instance per session). All further sub-pools share
+this allocator.
+
+Protecting the allocator allows sub pools to live in other threads 
+concurrently, Necessary for parallel processing of HTTP/2 streams. (As 
+alternative, using root pool for streams was tested, but resulted in poorer
+performance).
+
+Everything related to a h2_stream (and even the struct itself) is allocated
+from a new subpool. This guarantuees that all memory is recycled when the 
+stream is destroyed. Same holds true for h2_tasks, which get their own 
+sub pools.
+
+FILE BUCKET HANDLING
+--------------------
+Requests for static resources result most often in a single file bucket being
+send as body of the response. This would ideally be placed into eh h2_mplx,
+thus finished the h2_task and freeing the h2_worker for other things.
+
+This is difficult for the following 2 reasons:
+1. Finishing the h2_task will free pool and run registered cleanup functions
+   that closes the file. The file needs to traverse this boundary. Early
+   attempts at doing this nicely have failed.
+2. In load scenarios, we quickly run out of open file handles. With 100 max
+   parallel stream per connection, the process limits can be reached quite
+   easily.
+
+Solving 1) can be done by careful coding and good debugging of what the apache
+runtime does here. Solving 2) requires some kind of resource booking scheme
+inside the httpd child process, it seems.
+
+For now, mod_h2 reads files when placing the data into the h2_mplx. That means
+that files are completely read before the h2_task is finished and the h2_worker
+is available again. This limits the number of open files in buckets to the 
+number of h2_worker (by order of magnitude - there can be sub requests etc.).
+
+This works well and stable, but does not allow the transfer speeds of httpd's
+optimized HTTP/1 implementtion.
+
+
+DISCUSSION / OPEN QUESTIONS
+---------------------------
+- HTTP/2 Padding feature is not implemented. As RFC7540, Ch. 10.7 describes, 
+  using a fixed length padding is counter-productive, same as simple random
+  schemes. The ideal padding is supposed to be chosen by the application. So
+  maybe a response note or special header should determine it?
+- HTTP/2 Priority handling of streams is implemented in nghttp2, HOWEVER it
+  has no effect on h2_task scheduling. If there is a backlog of streams for
+  processing by workers, the stream with the highest priority should be
+  processed first. This information is currently not available via the nghttp2
+  API.
+
+
diff --git a/modules/http2/INSTALL b/modules/http2/INSTALL
new file mode 100644 (file)
index 0000000..261a7ce
--- /dev/null
@@ -0,0 +1,375 @@
+
+mod-h2 - a http/2 modules for apache httpd, Installation
+========================================================
+
+Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
+Inc.
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+Briefly, the shell command `./configure && make && make install'
+should configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.  Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below.  The lack of an optional feature in a given package is not
+necessarily a bug.  More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+1. `cd' to the directory containing the package's source code and type
+`./configure' to configure the package for your system.
+
+Running `configure' might take a while.  While running, it prints
+some messages telling which features it is checking for.
+
+2. Type `make' to compile the package.
+
+3. Optionally, type `make check' to run any self-tests that come with
+the package, generally using the just-built uninstalled binaries.
+
+4. Type `make install' to install the programs and any data files and
+documentation.  When installing into a prefix owned by root, it is
+recommended that the package be configured and built as a regular
+user, and only the `make install' phase executed with root
+privileges.
+
+5. Optionally, type `make installcheck' to repeat any self-tests, but
+this time using the binaries in their final installed location.
+This target does not install anything.  Running this target as a
+regular user, particularly if the prior `make install' required
+root privileges, verifies that the installation completed
+correctly.
+
+6. You can remove the program binaries and object files from the
+source code directory by typing `make clean'.  To also remove the
+files that `configure' created (so you can compile the package for
+a different kind of computer), type `make distclean'.  There is
+also a `make maintainer-clean' target, but that is intended mainly
+for the package's developers.  If you use it, you may have to get
+all sorts of other programs in order to regenerate files that came
+with the distribution.
+
+7. Often, you can also type `make uninstall' to remove the installed
+files again.  In practice, not all packages have tested that
+uninstallation works correctly, even though it is required by the
+GNU Coding Standards.
+
+8. Some packages, particularly those that use Automake, provide `make
+distcheck', which can by used by developers to test that all other
+targets like `make install' and `make uninstall' work correctly.
+This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+*Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.  This
+is known as a "VPATH" build.
+
+With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor.  Like
+this:
+
+./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+CPP="gcc -E" CXXCPP="g++ -E"
+
+This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.  In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+The first method involves providing an override variable for each
+affected directory.  For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'.  Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated.  The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+The second method involves providing the `DESTDIR' variable.  For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names.  The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters.  On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
+
+If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Some packages offer the ability to configure how verbose the
+execution of `make' will be.  For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+On HP-UX, the default C compiler is not ANSI C compatible.  If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+HP-UX `make' updates targets which have the same time stamps as
+their prerequisites, which makes it generally unusable when shipped
+generated files such as `configure' are involved.  Use GNU `make'
+instead.
+
+On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
+a workaround.  If GNU CC is not installed, it is therefore recommended
+to try
+
+./configure CC="cc"
+
+and if that doesn't work, try
+
+./configure CC="cc -nodtk"
+
+On Solaris, don't put `/usr/ucb' early in your `PATH'.  This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'.  So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'.  It is recommended to use the following options:
+
+./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+OS
+KERNEL-OS
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf limitation.  Until the limitation is lifted, you can use
+this workaround:
+
+CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+Print a summary of the options unique to this package's
+`configure', and exit.  The `short' variant lists options used
+only in the top level, while the `recursive' variant lists options
+also present in any nested packages.
+
+`--version'
+`-V'
+Print the version of Autoconf used to generate the `configure'
+script, and exit.
+
+`--cache-file=FILE'
+Enable the cache: use and save the results of the tests in FILE,
+traditionally `config.cache'.  FILE defaults to `/dev/null' to
+disable caching.
+
+`--config-cache'
+`-C'
+Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+Do not print messages saying which checks are being made.  To
+suppress all normal output, redirect it to `/dev/null' (any error
+messages will still be shown).
+
+`--srcdir=DIR'
+Look for the package's source code in directory DIR.  Usually
+`configure' can determine that directory automatically.
+
+`--prefix=DIR'
+Use DIR as the installation prefix.  *note Installation Names::
+for more details, including other options available for fine-tuning
+the installation locations.
+
+`--no-create'
+`-n'
+Run the configure checks, but stop before creating any output
+files.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
+
+
+
diff --git a/modules/http2/LICENSE b/modules/http2/LICENSE
new file mode 100644 (file)
index 0000000..46b2318
--- /dev/null
@@ -0,0 +1,202 @@
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction,
+and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by
+the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all
+other entities that control, are controlled by, or are under common
+control with that entity. For the purposes of this definition,
+"control" means (i) the power, direct or indirect, to cause the
+direction or management of such entity, whether by contract or
+otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity
+exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications,
+including but not limited to software source code, documentation
+source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical
+transformation or translation of a Source form, including but
+not limited to compiled object code, generated documentation,
+and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or
+Object form, made available under the License, as indicated by a
+copyright notice that is included in or attached to the work
+(an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object
+form, that is based on (or derived from) the Work and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship. For the purposes
+of this License, Derivative Works shall not include works that remain
+separable from, or merely link (or bind by name) to the interfaces of,
+the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including
+the original version of the Work and any modifications or additions
+to that Work or Derivative Works thereof, that is intentionally
+submitted to Licensor for inclusion in the Work by the copyright owner
+or by an individual or Legal Entity authorized to submit on behalf of
+the copyright owner. For the purposes of this definition, "submitted"
+means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems,
+and issue tracking systems that are managed by, or on behalf of, the
+Licensor for the purpose of discussing and improving the Work, but
+excluding communication that is conspicuously marked or otherwise
+designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity
+on behalf of whom a Contribution has been received by Licensor and
+subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the
+Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+(except as stated in this section) patent license to make, have made,
+use, offer to sell, sell, import, and otherwise transfer the Work,
+where such license applies only to those patent claims licensable
+by such Contributor that are necessarily infringed by their
+Contribution(s) alone or by combination of their Contribution(s)
+with the Work to which such Contribution(s) was submitted. If You
+institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work
+or a Contribution incorporated within the Work constitutes direct
+or contributory patent infringement, then any patent licenses
+granted to You under this License for that Work shall terminate
+as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+Work or Derivative Works thereof in any medium, with or without
+modifications, and in Source or Object form, provided that You
+meet the following conditions:
+
+(a) You must give any other recipients of the Work or
+Derivative Works a copy of this License; and
+
+(b) You must cause any modified files to carry prominent notices
+stating that You changed the files; and
+
+(c) You must retain, in the Source form of any Derivative Works
+that You distribute, all copyright, patent, trademark, and
+attribution notices from the Source form of the Work,
+excluding those notices that do not pertain to any part of
+the Derivative Works; and
+
+(d) If the Work includes a "NOTICE" text file as part of its
+distribution, then any Derivative Works that You distribute must
+include a readable copy of the attribution notices contained
+within such NOTICE file, excluding those notices that do not
+pertain to any part of the Derivative Works, in at least one
+of the following places: within a NOTICE text file distributed
+as part of the Derivative Works; within the Source form or
+documentation, if provided along with the Derivative Works; or,
+within a display generated by the Derivative Works, if and
+wherever such third-party notices normally appear. The contents
+of the NOTICE file are for informational purposes only and
+do not modify the License. You may add Your own attribution
+notices within Derivative Works that You distribute, alongside
+or as an addendum to the NOTICE text from the Work, provided
+that such additional attribution notices cannot be construed
+as modifying the License.
+
+You may add Your own copyright statement to Your modifications and
+may provide additional or different license terms and conditions
+for use, reproduction, or distribution of Your modifications, or
+for any such Derivative Works as a whole, provided Your use,
+reproduction, and distribution of the Work otherwise complies with
+the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+any Contribution intentionally submitted for inclusion in the Work
+by You to the Licensor shall be under the terms and conditions of
+this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify
+the terms of any separate license agreement you may have executed
+with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+names, trademarks, service marks, or product names of the Licensor,
+except as required for reasonable and customary use in describing the
+origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+agreed to in writing, Licensor provides the Work (and each
+Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied, including, without limitation, any warranties or conditions
+of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+PARTICULAR PURPOSE. You are solely responsible for determining the
+appropriateness of using or redistributing the Work and assume any
+risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+whether in tort (including negligence), contract, or otherwise,
+unless required by applicable law (such as deliberate and grossly
+negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special,
+incidental, or consequential damages of any character arising as a
+result of this License or out of the use or inability to use the
+Work (including but not limited to damages for loss of goodwill,
+work stoppage, computer failure or malfunction, or any and all
+other commercial damages or losses), even if such Contributor
+has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+the Work or Derivative Works thereof, You may choose to offer,
+and charge a fee for, acceptance of support, warranty, indemnity,
+or other liability obligations and/or rights consistent with this
+License. However, in accepting such obligations, You may act only
+on Your own behalf and on Your sole responsibility, not on behalf
+of any other Contributor, and only if You agree to indemnify,
+defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason
+of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following
+boilerplate notice, with the fields enclosed by brackets "[]"
+replaced with your own identifying information. (Don't include
+the brackets!)  The text should be enclosed in the appropriate
+comment syntax for the file format. We also recommend that a
+file or class name and description of purpose be included on the
+same "printed page" as the copyright notice for easier
+identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/modules/http2/Makefile.am b/modules/http2/Makefile.am
new file mode 100644 (file)
index 0000000..5668b35
--- /dev/null
@@ -0,0 +1,48 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+SUBDIRS         = @BUILD_SUBDIRS@
+DIST_SUBDIRS    = mod_h2 sandbox setup
+
+ACLOCAL_AMFLAGS = -I m4
+
+
+dist_doc_DATA   = README
+
+
+.PHONY: test loadtest start stop restart
+
+start:
+       make -C sandbox start
+
+restart:
+       make -C sandbox restart
+
+stop:
+       make -C sandbox stop
+
+test:
+       make -C sandbox test
+
+loadtest:
+       make -C sandbox loadtest
+
+mpm_worker:
+       make -C sandbox mpm_worker
+
+mpm_event:
+       make -C sandbox mpm_event
+
+mpm_prefork:
+       make -C sandbox mpm_prefork
diff --git a/modules/http2/NEWS b/modules/http2/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/modules/http2/README b/modules/http2/README
new file mode 100644 (file)
index 0000000..3389200
--- /dev/null
@@ -0,0 +1 @@
+See README.md
\ No newline at end of file
diff --git a/modules/http2/README.md b/modules/http2/README.md
new file mode 100644 (file)
index 0000000..b6441c8
--- /dev/null
@@ -0,0 +1,205 @@
+
+#mod_h2 - http/2 for Apache httpd
+
+Copyright (C) 2015 greenbytes GmbH
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without warranty of any kind. See LICENSE for details.
+
+
+This repository contains a Apache httpd module implementing the HTTP2
+protocol. It uses nghttp2 (https://nghttp2.org) as base engine and connects
+it with the Apache infrastructure.
+
+
+##Status
+In development. Use at your own risk. See [Known Problems](#known-problems) below for a list
+of things you might encounter.
+
+##Tested Platforms
+* OS: Ubuntu 14.04, OS X 10.10
+* Apache httpd 2.4.12 (patch needed)
+* Openssl 1.0.1 + 1.0.2
+
+If someone wants to test other platforms or contribute adapations in a
+github pull request, she is more than welcome.
+
+
+##Features
+This module supports the protocols "h2" (HTTP2 over TLS) and "h2c" (HTTP2 over plain
+HTTP connections via Upgrade). You can enable it for the whole server or
+for specific virtual hosts only. More on this below on "Configuration".
+
+Specifically, the protocols "h2", "h2-16", "h2-14" and its "h2c" cousins
+are announced to clients. Support for "h2-14" and "h2-16" is expected to
+disappear silently as these are no standard and are currently being used
+for the interop testing phase only.
+
+##Configuration
+The test setup in test/conf/* that gets installed in gen/install for the
+local httpd build contains some simple examples of how this module can
+be configured.
+
+There are several configuration commands available when mod_h2 is loaded,
+such as:
+
+* H2Engine (on/off), "on"    to enable HTTP/2 protocol handling, default: off
+* H2MaxSessionStreams n      maximum number of open streams per session, default: 100
+* H2InitialWindowSize n      initial window size on client DATA, default: 16k
+* H2MaxHeaderListSize n      maximum acceptable size of request headers, default: 64k
+* H2MinWorkers n             minimum number of worker threads per child, default: mpm configured MaxWorkers/2
+* H2MaxWorkers n             maximum number of worker threads per child, default: mpm configured thread limit/2
+* H2StreamMaxMemSize n       maximum number of bytes buffered in memory for a stream, default: 64k
+* H2AltSvc name=host:port    Announce an "alternate service" to clients (see https://http2.github.io/http2-spec/alt-svc.html for details), default: empty
+* H2AltSvcMaxAge n           number of seconds Alt-Svc information is valid, default: will not be sent, specification defaults to 24h
+* H2SerializeHeaders (on/off), "off"   serialize/parse request+response headers for streams, as if they arrived in HTTP/1 format. When off, certain parts of httpd core filters are disabled/replaced to allow for a more efficient handling. 
+* H2HackMpmEvent (on/off), "on"        performs a hack on internal connection in order to make mpm_event working, has no effect on other mpm modules
+* H2Direct (on/off), "on"    to enable h2c direct mode on a non-TLS host, default: off
+
+All these configuration parameters can be set on servers/virtual hosts and
+are not available on directory level. Note that Worker configuration is
+only relevant on the base apache server and will be read - but ignored -
+on any virtual hosts.
+
+
+##Dependencies
+1. The module is written in plain C and links to libnghttp2 and the
+apache runtime. Currently, only nghttp2 version 0.7.x are supported. nghttp2 v1.0.0 introduced a incompatible API change. mod_h2 will change once there is a curl release working with 1.0.0. That release of mod_h2 will then stop support for nghttp2 0.7.x. Heads up.
+1. For "h2" support - the part of HTTP2 that uses TLS - a patched mod_ssl
+needs to be present in the server. The patch is available in httpd/patches
+and automatically applied in sandbox mode.
+1. For ALPN/NPN protocol negotiation (the basic TLS HELLO part) to work,
+at least OpenSSL 1.0.1 is needed (OpenSSL 1.0.2 perferred).
+1. mod_h2 is tested with the `mpm_worker` module. The `mpm_event` module is supported by a hack at the moment, handle with care.
+
+
+##Installation
+mod_h2 is using autoconf/automake for configuration and build handling. If you
+have a git checkout, refer to 'Build from git' below. If you have a release
+extracted, you need to:
+```
+> cd mod_h2-x.x.x
+> ./configure
+> make
+```
+
+For general handling of 'configure', see INSTALL. For mod_h2 specifically,
+there are two arguments to know:
+* `--enable-sandbox`     build a complete sandbox installation with own httpd, own libnghttp2
+* `--enable-werror`      build with tons of compiler diagnostics enabled
+* `--with-apxs=<pathtoapxs>` for a non-sandboxed installation where the apxs (from the apache development environment) is in an unusual location.
+
+If you run 'configure' without arguments, it assumes a non-sandbox'ed built
+where apxs and libnghttp2 are properly installed.
+
+
+###Sandbox Installation:
+
+The sandbox installation puts everything in ./gen/install: httpd, nghttp2, curl
+and other fine things. For testing the module, or just trying it out, this
+has the following advantages:
+* conflicts with "wrong" versions already installed on your system are avoided
+* you can do the installation and test without root privileges
+* certain patches can be applied that are necessary for all features to work, see "Dependencies"."
+
+
+##Build from git
+Building from git is easy, but please be sure that at least autoconf 2.68 is
+used::
+```
+> autoreconf -i
+> automake
+> autoconf
+> ./configure
+> make
+```
+
+##Supported Platforms
+mod_h2 has been developed under Ubuntu 14.04 LTS and OS X 10.10. The module
+itself depends only on an installed APXS (the Apache runtime platform) and
+libnghttp2. As long as both are present, the module itself should build
+just fine.
+
+Ubuntu :Install the prerequisite software. On a 14.04 LTS server, that should be:
+```
+> sudo apt-get install git gcc g++ libpcre3-dev libcunit1-dev libev-dev libjansson-dev libjemalloc-dev cython make binutils autoconf automake autotools-dev libtool pkg-config zlib1g-dev libssl-dev libxml2-dev libevent-dev python3.4-dev libevent-openssl-2.0-5 php5-cgi
+```
+
+OS X: on OS X 10.10, building the project requires a homebrew installation and the following packages installed via brew:
+* pkg-config
+* for httpd the Makefile will download and install:
+    * pcre
+    * apr + apr-util
+    * openssl
+  exact versions and download urls in httpd/Makefile
+* for nghttp2 the Makefile will download and install:
+    * zlib
+    * libev
+  exact versions and download urls in httpd/Makefile
+* for sandbox tests you will need php5-cgi from homebrew
+
+##Architecture, Limits, Details
+See DISCUSS.
+
+
+##Sandbox Testing
+The sandbox build contains some test cases. In order to run those, you
+need to:
+```
+> make
+> make install
+> make test
+```
+The sandbox creates its own httpd and nghttp2 installation in gen/install
+and configures httpd to run on ports 12345+12346 (TLS). It also generates
+a self-signed certificate for the servers under the name test.example.org
+and test-ser.example.org.
+You should make entries in /etc/hosts like
+```
+127.0.0.1       test.example.org        test
+127.0.0.1       test-ser.example.org    test
+```
+for tests to work properly.
+
+Another issue is testing with browsers like Chrome or Firefox. If you point
+them at test.example.org, they will complain about the self-signed certificate,
+offer you to connect anyway and, if you choose that, refuse to work. I think
+they have a much stricter cert checking for HTTP/2 and the UI needs an update
+here.
+
+I myself configure an additional site into the sandbox server with a real
+certificate and test browsers successfully there. But I cannot share this
+certificate with the world. If there is a better way to test browser interop,
+I am happy to be given pointers.
+
+#Known Issues
+* If you use the "prefork" mpm, there are reported problems with using mod_h2 and mod_prody/mod_rewrite against another server which disappear when using mpm_event or mpm_worker in the otherwise unchanged configuration.
+* If you test chrome/firefox against a httpd with mod_h2 and get "ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY", this means that the browser considers the installed SSL certificates as not good enough to use HTTP/2. This will happen with the sandbox installation, as that one has only a self-signed certificate. If you disable mod_h2, chrome/firefox will seem to be working fine again against your server. This is due to the fact that SSL requirements are, for backward compatibility, relaxed when talking HTTP/1. Unfortunately, chrome/firefox do currently not offer a better user experience in indicating what part of the certificate was considered inadequate. For example, certain signing algorithms in combination with expiration dates are no longer accepted. This becomes pretty hard to debug for someone not fluent in TLS and PKI.
+* Some modules will not be fully compatible with HTTP/2 connections. mod_logio, for example, will not properly report the accumulated traffic per connection as requests are handled in sub-connecition and that data is never aggregated.
+
+##TODO
+* Thanks to the excellent nghttp2, the module currently supports stream priority
+handling, but nghttp2 offers at the moment (v0.7.15) no way to use the prio
+information for request scheduling.
+* mpm_event: supported by a hack atm. Needs an official patch with an Optional
+function
+* http trailers are not implemented
+* mod_h2 removes reqtimeout input filter for its connection. 
+  
+
+##Licensing
+Please see the file called LICENSE.
+
+
+##Credits
+This work has been funded by the GSM Association (http://gsma.com). The module
+itself was heavily influenced by mod_spdy, the Google implementation of their
+SPDY protocol. And without Tatsuhiro Tsujikawa excellent nghttp2 work, this
+would not have been possible.
+
+
+Münster, 20.05.2015,
+
+Stefan Eissing, greenbytes GmbH
diff --git a/modules/http2/configure.ac b/modules/http2/configure.ac
new file mode 100644 (file)
index 0000000..39c7b57
--- /dev/null
@@ -0,0 +1,211 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AC_PREREQ([2.69])
+AC_INIT([mod_h2], [0.7.3], [stefan.eissing@greenbytes.de])
+
+LT_PREREQ([2.2.6])
+LT_INIT()
+dnl See versioning rule:
+dnl  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+AC_SUBST(LT_CURRENT, 11)
+AC_SUBST(LT_REVISION, 0)
+AC_SUBST(LT_AGE, 6)
+
+major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
+minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
+patch=`echo $PACKAGE_VERSION |cut -d. -f3 | cut -d- -f1 | sed -e "s/[^0-9]//g"`
+
+PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
+
+AC_SUBST(PACKAGE_VERSION_NUM)
+
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+AC_CANONICAL_TARGET
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([subdir-objects no-define])
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_CONFIG_SRCDIR([mod_h2/h2_config.c])
+AC_CONFIG_HEADERS([mod_h2/config.h])
+
+AC_ARG_ENABLE([werror],
+    [AS_HELP_STRING([--enable-werror],
+                    [Turn on compile time warnings])],
+    [werror=$enableval], [werror=no])
+
+AC_ARG_ENABLE([sandbox], [AS_HELP_STRING([--enable-sandbox],
+    [Build a sandbox installation [default=no]])],
+    [case "${enableval}" in
+        yes) build_mode=sandbox ;;
+        no)  build_mode=extern ;;
+        *) AC_MSG_ERROR([bad value ${enableval} for --enable-sandbox]) ;;
+    esac],
+    [build_mode=extern])
+
+AC_ARG_WITH([apxs], [AS_HELP_STRING([--with-apxs],
+    [Use APXS executable [default=check]])],
+    [request_apxs=$withval], [request_apxs=check])
+
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_CC_STDC
+
+if test "x$werror" != "xno"; then
+    AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
+    AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
+    AX_CHECK_COMPILE_FLAG([-Werror], [CFLAGS="$CFLAGS -Werror"])
+    AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CFLAGS="$CFLAGS -Wmissing-prototypes"])
+    AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CFLAGS="$CFLAGS -Wstrict-prototypes"])
+    AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CFLAGS="$CFLAGS -Wmissing-declarations"])
+    AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CFLAGS="$CFLAGS -Wpointer-arith"])
+    # we like to use such things...
+    #AX_CHECK_COMPILE_FLAG([-Wdeclaration-after-statement], [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
+    AX_CHECK_COMPILE_FLAG([-Wformat-security], [CFLAGS="$CFLAGS -Wformat-security"])
+    AX_CHECK_COMPILE_FLAG([-Wwrite-strings], [CFLAGS="$CFLAGS -Wwrite-strings"])
+    AX_CHECK_COMPILE_FLAG([-Wshadow], [CFLAGS="$CFLAGS -Wshadow"])
+    AX_CHECK_COMPILE_FLAG([-Winline], [CFLAGS="$CFLAGS -Winline"])
+    AX_CHECK_COMPILE_FLAG([-Wnested-externs], [CFLAGS="$CFLAGS -Wnested-externs"])
+    AX_CHECK_COMPILE_FLAG([-Wfloat-equal], [CFLAGS="$CFLAGS -Wfloat-equal"])
+    AX_CHECK_COMPILE_FLAG([-Wundef], [CFLAGS="$CFLAGS -Wundef"])
+    AX_CHECK_COMPILE_FLAG([-Wendif-labels], [CFLAGS="$CFLAGS -Wendif-labels"])
+    AX_CHECK_COMPILE_FLAG([-Wempty-body], [CFLAGS="$CFLAGS -Wempty-body"])
+# does not make sense with APR as it prefers char* to void*
+#    AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
+    AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
+    AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
+    AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
+    AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
+    AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
+    AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS="$CFLAGS -Wattributes"])
+    AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"])
+    AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS="$CFLAGS -Wshorten-64-to-32"])
+fi
+
+
+AC_MSG_NOTICE("configuring for $build_mode build")
+
+if test x"$build_mode" = "xsandbox"; then
+    if test x"$request_apxs" != "xcheck"; then
+        AC_MSG_ERROR([when sandbox mode is enabled, specifying apxs is not allowed])
+    fi
+
+    # easy, we know where everything will be
+    #
+    export BUILD_SUBDIRS="sandbox mod_h2"
+
+    prefix="$PWD/sandbox/install"
+    APXS="$prefix/bin/apxs"
+    LDFLAGS="$LDFLAGS -L${prefix}/lib"
+    LIBS="$LIBS -lnghttp2"
+    CPPFLAGS="$CPPFLAGS -I${prefix}/include"
+    export SYSCONF_DIR="$prefix/conf"
+    export LIBEXEC_DIR="$prefix/modules"
+    # On Ubuntu, we have a propblem with APR 1.5.1 and system includes that
+    # causes compilation to abort since no definition of PATH_MAX can be found.
+    # In sandbox build, we just define it therefore, as a quick workaround
+    CPPFLAGS="$CPPFLAGS -DPATH_MAX=4096"
+
+    # we use a new nghttp2 in the sandbox which has these features
+    NGHTTP2_HAS_DATA_CB=1
+
+else
+    # production, we need to find where the apxs is. which then
+    # can tell us the various directories we need.
+    #
+    if test x"$request_apxs" = "xcheck"; then
+        AC_PATH_PROG([APXS], [apxs])
+        if test "x${APXS}" = "x"; then
+            AC_MSG_ERROR("no APXS installation found")
+        fi
+    else
+        APXS="$request_apxs"
+        AC_MSG_NOTICE("using APXS=$APXS as configured")
+    fi
+
+    export BUILD_SUBDIRS="mod_h2 setup"
+
+    prefix="$($APXS -q prefix)"
+    LDFLAGS="$LDFLAGS -L$($APXS -q libdir)"
+    CPPFLAGS="$CPPFLAGS -I$($APXS -q includedir) -I$($APXS -q APR_INCLUDEDIR)"
+    export SYSCONF_DIR="$($APXS -q sysconfdir)"
+    export LIBEXEC_DIR="$($APXS -q LIBEXECDIR)"
+
+    # We need nghttp2 to be in our link path, check for it.
+    #
+    AC_CHECK_LIB([nghttp2], [nghttp2_session_server_new2], ,
+    [AC_MSG_ERROR("library nghttp2 not found")])
+
+    AC_CHECK_LIB([nghttp2], [nghttp2_session_callbacks_set_send_data_callback], 
+        [NGHTTP2_HAS_DATA_CB=1], [NGHTTP2_HAS_DATA_CB=0])
+
+fi
+
+# Checks for header files.
+AC_CHECK_HEADERS([ \
+    assert.h \
+    stddef.h \
+])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT32_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_CHECK_FUNCS([memmove memset strcasecmp strchr])
+
+AC_CHECK_PROG([A2ENMOD],[a2enmod])
+
+# substitution in generated files
+AC_SUBST(BUILD_SUBDIRS)
+AC_SUBST(SYSCONF_DIR)
+AC_SUBST(LIBEXEC_DIR)
+AC_SUBST(NGHTTP2_HAS_DATA_CB)
+
+AC_CONFIG_FILES([
+    Makefile
+    mod_h2/Makefile
+    mod_h2/h2_version.h
+    sandbox/Makefile 
+    setup/Makefile
+])
+
+AC_OUTPUT
+
+AC_MSG_NOTICE([summary of build options:
+
+    Version:        ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE
+    Host type:      ${host}
+    Build Type:     ${build_mode}
+    Install prefix: ${prefix}
+    APXS:           ${APXS}
+    C compiler:     ${CC}
+    CFLAGS:         ${CFLAGS}
+    WARNCFLAGS:     ${WARNCFLAGS}
+    LDFLAGS:        ${LDFLAGS}
+    LIBS:           ${LIBS}
+    CPPFLAGS:       ${CPPFLAGS}
+    C preprocessor: ${CPP}
+    BUILD_SUBDIRS   ${BUILD_SUBDIRS}
+    a2enmod         ${A2ENMOD:--}
+])
diff --git a/modules/http2/m4/ax_check_compile_flag.m4 b/modules/http2/m4/ax_check_compile_flag.m4
new file mode 100644 (file)
index 0000000..51df0c0
--- /dev/null
@@ -0,0 +1,74 @@
+# ===========================================================================
+#   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the current language's compiler
+#   or gives an error.  (Warnings, however, are ignored)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
+#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
+#   force the compiler to issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 3
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+    [AS_VAR_SET(CACHEVAR,[yes])],
+    [AS_VAR_SET(CACHEVAR,[no])])
+  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/modules/http2/m4/h2.m4 b/modules/http2/m4/h2.m4
new file mode 100644 (file)
index 0000000..8ba0b2b
--- /dev/null
@@ -0,0 +1 @@
+# just so it is not empty
diff --git a/modules/http2/mod-h2.xcodeproj/project.pbxproj b/modules/http2/mod-h2.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..e9668f7
--- /dev/null
@@ -0,0 +1,982 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 46;
+       objects = {
+
+/* Begin PBXFileReference section */
+               B20946CA1B25D4430041992F /* test_alt_host.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = test_alt_host.sh; sourceTree = "<group>"; };
+               B20946CC1B2865BB0041992F /* h2_alpn.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_alpn.c; sourceTree = "<group>"; };
+               B20946CD1B2865BB0041992F /* h2_alpn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_alpn.h; sourceTree = "<group>"; };
+               B20946CE1B29D8C30041992F /* sni_misdirect.patch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = sni_misdirect.patch; sourceTree = "<group>"; };
+               B20E441F1ACC07E8003D21AE /* test_curl_get.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test_curl_get.sh; sourceTree = "<group>"; };
+               B20E44201ACC088E003D21AE /* test_curl_post.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test_curl_post.sh; sourceTree = "<group>"; };
+               B21BFCF91B023B840031EBCD /* h2_task_queue.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_task_queue.c; sourceTree = "<group>"; };
+               B21BFCFA1B023B840031EBCD /* h2_task_queue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_task_queue.h; sourceTree = "<group>"; };
+               B225490C1A6EAC37004BDEC9 /* mod_h2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mod_h2.c; sourceTree = "<group>"; };
+               B225490D1A6EAE9F004BDEC9 /* h2_config.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_config.c; sourceTree = "<group>"; };
+               B225490E1A6EAE9F004BDEC9 /* h2_config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_config.h; sourceTree = "<group>"; };
+               B225491E1A6FB8DF004BDEC9 /* h2_h2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_h2.c; sourceTree = "<group>"; };
+               B225491F1A6FB8DF004BDEC9 /* h2_h2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_h2.h; sourceTree = "<group>"; };
+               B22549331A6FBEF1004BDEC9 /* h2_ctx.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_ctx.c; sourceTree = "<group>"; };
+               B22549341A6FBEF1004BDEC9 /* h2_ctx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_ctx.h; sourceTree = "<group>"; };
+               B22549361A6FCCAA004BDEC9 /* mod_h2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mod_h2.h; sourceTree = "<group>"; };
+               B22549371A6FCE13004BDEC9 /* h2_private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_private.h; sourceTree = "<group>"; };
+               B22549381A710329004BDEC9 /* h2_session.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_session.c; sourceTree = "<group>"; };
+               B22549391A710329004BDEC9 /* h2_session.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_session.h; sourceTree = "<group>"; };
+               B225493A1A711985004BDEC9 /* h2_conn_io.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_conn_io.c; sourceTree = "<group>"; };
+               B225493B1A711985004BDEC9 /* h2_conn_io.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_conn_io.h; sourceTree = "<group>"; };
+               B225493C1A713D18004BDEC9 /* h2_util.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_util.c; sourceTree = "<group>"; };
+               B225493D1A713D18004BDEC9 /* h2_util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_util.h; sourceTree = "<group>"; };
+               B227966E1AEE93B000376B40 /* httpd-npn.unified.diff.patch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "httpd-npn.unified.diff.patch"; sourceTree = "<group>"; };
+               B22D25281AA89A0B0041D8E5 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
+               B24CB1221AC94D6A0057413B /* h2_alt_svc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_alt_svc.c; sourceTree = "<group>"; };
+               B24CB1231AC94D6A0057413B /* h2_alt_svc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_alt_svc.h; sourceTree = "<group>"; };
+               B24CB1241AC96BFE0057413B /* test_curl_altsvc.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test_curl_altsvc.sh; sourceTree = "<group>"; };
+               B2546D881A7F71F80033A875 /* h2_task.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_task.c; sourceTree = "<group>"; };
+               B2546D891A7F71F80033A875 /* h2_task.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_task.h; sourceTree = "<group>"; };
+               B2546D8E1A7FA7D70033A875 /* h2_task_output.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_task_output.c; sourceTree = "<group>"; };
+               B2546D8F1A7FA7D70033A875 /* h2_task_output.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_task_output.h; sourceTree = "<group>"; };
+               B2546D971A7FC2A60033A875 /* h2_from_h1.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_from_h1.c; sourceTree = "<group>"; };
+               B2546D981A7FC2A60033A875 /* h2_from_h1.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_from_h1.h; sourceTree = "<group>"; };
+               B2546D991A81079D0033A875 /* h2_stream_set.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_stream_set.c; sourceTree = "<group>"; };
+               B2546D9A1A81079D0033A875 /* h2_stream_set.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_stream_set.h; sourceTree = "<group>"; };
+               B256C4BA1ADD5FF10042C760 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
+               B271425E1AB9DD59004EF82E /* h2_io_set.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h2_io_set.c; sourceTree = "<group>"; };
+               B271425F1AB9DD59004EF82E /* h2_io_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h2_io_set.h; sourceTree = "<group>"; };
+               B27142601AB9DD76004EF82E /* h2_io.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_io.c; sourceTree = "<group>"; };
+               B27142611AB9DD76004EF82E /* h2_io.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_io.h; sourceTree = "<group>"; };
+               B272F2311B1E13D8007A20A5 /* httpd-2.4.x-alpn-v4.patch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "httpd-2.4.x-alpn-v4.patch"; sourceTree = "<group>"; };
+               B27BBD2D1A6575C200C58A41 /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+               B27D32E81A9487B4003DBAF4 /* h2_mplx.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_mplx.c; sourceTree = "<group>"; };
+               B27D32E91A9487B4003DBAF4 /* h2_mplx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_mplx.h; sourceTree = "<group>"; };
+               B284ACC91A7B9DF900C35863 /* h2_task_input.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_task_input.c; sourceTree = "<group>"; };
+               B284ACCA1A7B9DF900C35863 /* h2_task_input.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_task_input.h; sourceTree = "<group>"; };
+               B2A6EF171A9B598B005DFC5B /* h2_request.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_request.c; sourceTree = "<group>"; };
+               B2A6EF181A9B598B005DFC5B /* h2_request.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_request.h; sourceTree = "<group>"; };
+               B2A6EF281A9C79A6005DFC5B /* h2_upgrade.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_upgrade.c; sourceTree = "<group>"; };
+               B2A6EF291A9C79A6005DFC5B /* h2_upgrade.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_upgrade.h; sourceTree = "<group>"; };
+               B2A6EF2A1A9E3A93005DFC5B /* INSTALL */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = INSTALL; sourceTree = "<group>"; };
+               B2A6EF2C1A9F2452005DFC5B /* DISCUSS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DISCUSS; sourceTree = "<group>"; };
+               B2AABE611AC5A9B400A90B72 /* h2_version.h.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = h2_version.h.in; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.c; };
+               B2B170B31B2EC73400EDC007 /* test.example.org.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test.example.org.conf; sourceTree = "<group>"; };
+               B2B170B41B2F02C100EDC007 /* testrun */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = testrun; sourceTree = "<group>"; };
+               B2B170B61B303B0C00EDC007 /* noh2.example.org.x509.input */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = noh2.example.org.x509.input; sourceTree = "<group>"; };
+               B2B170B81B316FB100EDC007 /* aaa-noh2.example.org.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "aaa-noh2.example.org.conf"; sourceTree = "<group>"; };
+               B2B170B91B31BF7000EDC007 /* httpd-2.4.12-alpn-v5.patch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "httpd-2.4.12-alpn-v5.patch"; sourceTree = "<group>"; };
+               B2C16D251A779C55000B2297 /* h2_stream.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_stream.c; sourceTree = "<group>"; };
+               B2C16D261A779C55000B2297 /* h2_stream.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_stream.h; sourceTree = "<group>"; };
+               B2C631081B383EA800127D1E /* test-ser.example.org.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "test-ser.example.org.conf"; sourceTree = "<group>"; };
+               B2C6310C1B393CA100127D1E /* httpd-alpn-v4-v5.patch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "httpd-alpn-v4-v5.patch"; sourceTree = "<group>"; };
+               B2C63B0E1B39421B00127D1E /* openssl-1.0.2-alpn.patch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "openssl-1.0.2-alpn.patch"; sourceTree = "<group>"; };
+               B2C63B101B3BF78C00127D1E /* mpm_worker.load */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mpm_worker.load; sourceTree = "<group>"; };
+               B2C63B121B3BF7CC00127D1E /* mpm_event.load */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mpm_event.load; sourceTree = "<group>"; };
+               B2C63B131B3BF7CC00127D1E /* mpm_prefork.load */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mpm_prefork.load; sourceTree = "<group>"; };
+               B2CB610E1A88BB9F00D270A6 /* h2_conn.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_conn.c; sourceTree = "<group>"; };
+               B2CB610F1A88BB9F00D270A6 /* h2_conn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_conn.h; sourceTree = "<group>"; };
+               B2CB61101A8A0FB400D270A6 /* h2_response.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_response.c; sourceTree = "<group>"; };
+               B2CB61111A8A0FB400D270A6 /* h2_response.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_response.h; sourceTree = "<group>"; };
+               B2CB61141A8B698A00D270A6 /* h2_worker.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_worker.c; sourceTree = "<group>"; };
+               B2CB61151A8B698A00D270A6 /* h2_worker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_worker.h; sourceTree = "<group>"; };
+               B2CB61161A8B699E00D270A6 /* h2_workers.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_workers.c; sourceTree = "<group>"; };
+               B2CB61171A8B699E00D270A6 /* h2_workers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_workers.h; sourceTree = "<group>"; };
+               B2E5D5081AADAD28001FD280 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+               B2E5D7841AAEEF8C001FD280 /* configure.ac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = configure.ac; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.sh; };
+               B2E5D7851AAF1D87001FD280 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+               B2E5D7861AAF1F4D001FD280 /* AUTHORS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS; sourceTree = "<group>"; };
+               B2E5D7871AAF1F4D001FD280 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
+               B2E5D7881AAF1F4D001FD280 /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; };
+               B2E5D7891AAF1F4D001FD280 /* NEWS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NEWS; sourceTree = "<group>"; };
+               B2E6DF771AC2E55800D557D8 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+               B2E6DF781AC2E6A700D557D8 /* install-config.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "install-config.sh"; sourceTree = "<group>"; };
+               B2E6DF7B1AC2EF6E00D557D8 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
+               B2E6DF7C1AC2EF6E00D557D8 /* get-openssl-latest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "get-openssl-latest.sh"; sourceTree = "<group>"; };
+               B2E6DF7D1AC2EF6E00D557D8 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+               B2E6DF9E1AC2EF6E00D557D8 /* pcre-8.36.tar.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = "pcre-8.36.tar.gz"; sourceTree = "<group>"; };
+               B2E6DFA31AC2EF6E00D557D8 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+               B2E6FDF81AC2EFA200D557D8 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+               B2E6FDFA1AC2EFA200D557D8 /* httpd.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = httpd.conf; sourceTree = "<group>"; };
+               B2E6FDFB1AC2EFA200D557D8 /* modules.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = modules.conf; sourceTree = "<group>"; };
+               B2E6FDFD1AC2EFA200D557D8 /* mod-h2.greenbytes.de.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "mod-h2.greenbytes.de.conf"; sourceTree = "<group>"; };
+               B2E6FE001AC2EFA200D557D8 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
+               B2E6FE011AC2EFA200D557D8 /* ca.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ca.pem; sourceTree = "<group>"; };
+               B2E6FE021AC2EFA200D557D8 /* extensions.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = extensions.conf; sourceTree = "<group>"; };
+               B2E6FE031AC2EFA200D557D8 /* mod-h2.greenbytes.de.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "mod-h2.greenbytes.de.key"; sourceTree = "<group>"; };
+               B2E6FE041AC2EFA200D557D8 /* mod-h2.greenbytes.de.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "mod-h2.greenbytes.de.pem"; sourceTree = "<group>"; };
+               B2E6FE051AC2EFA200D557D8 /* test.example.org.x509.input */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test.example.org.x509.input; sourceTree = "<group>"; };
+               B2E6FE091AC2EFA200D557D8 /* 001.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = 001.html; sourceTree = "<group>"; };
+               B2E6FE0A1AC2EFA200D557D8 /* 002.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 002.jpg; sourceTree = "<group>"; };
+               B2E6FE0C1AC2EFA200D557D8 /* 003_img.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 003_img.jpg; sourceTree = "<group>"; };
+               B2E6FE0D1AC2EFA200D557D8 /* 003.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = 003.html; sourceTree = "<group>"; };
+               B2E6FE0F1AC2EFA200D557D8 /* gophertiles.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles.jpg; sourceTree = "<group>"; };
+               B2E6FE101AC2EFA200D557D8 /* gophertiles_002.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_002.jpg; sourceTree = "<group>"; };
+               B2E6FE111AC2EFA200D557D8 /* gophertiles_003.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_003.jpg; sourceTree = "<group>"; };
+               B2E6FE121AC2EFA200D557D8 /* gophertiles_004.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_004.jpg; sourceTree = "<group>"; };
+               B2E6FE131AC2EFA200D557D8 /* gophertiles_005.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_005.jpg; sourceTree = "<group>"; };
+               B2E6FE141AC2EFA200D557D8 /* gophertiles_006.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_006.jpg; sourceTree = "<group>"; };
+               B2E6FE151AC2EFA200D557D8 /* gophertiles_007.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_007.jpg; sourceTree = "<group>"; };
+               B2E6FE161AC2EFA200D557D8 /* gophertiles_008.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_008.jpg; sourceTree = "<group>"; };
+               B2E6FE171AC2EFA200D557D8 /* gophertiles_009.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_009.jpg; sourceTree = "<group>"; };
+               B2E6FE181AC2EFA200D557D8 /* gophertiles_010.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_010.jpg; sourceTree = "<group>"; };
+               B2E6FE191AC2EFA200D557D8 /* gophertiles_011.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_011.jpg; sourceTree = "<group>"; };
+               B2E6FE1A1AC2EFA200D557D8 /* gophertiles_012.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_012.jpg; sourceTree = "<group>"; };
+               B2E6FE1B1AC2EFA200D557D8 /* gophertiles_013.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_013.jpg; sourceTree = "<group>"; };
+               B2E6FE1C1AC2EFA200D557D8 /* gophertiles_014.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_014.jpg; sourceTree = "<group>"; };
+               B2E6FE1D1AC2EFA200D557D8 /* gophertiles_015.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_015.jpg; sourceTree = "<group>"; };
+               B2E6FE1E1AC2EFA200D557D8 /* gophertiles_016.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_016.jpg; sourceTree = "<group>"; };
+               B2E6FE1F1AC2EFA200D557D8 /* gophertiles_017.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_017.jpg; sourceTree = "<group>"; };
+               B2E6FE201AC2EFA200D557D8 /* gophertiles_018.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_018.jpg; sourceTree = "<group>"; };
+               B2E6FE211AC2EFA200D557D8 /* gophertiles_019.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_019.jpg; sourceTree = "<group>"; };
+               B2E6FE221AC2EFA200D557D8 /* gophertiles_020.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_020.jpg; sourceTree = "<group>"; };
+               B2E6FE231AC2EFA200D557D8 /* gophertiles_021.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_021.jpg; sourceTree = "<group>"; };
+               B2E6FE241AC2EFA200D557D8 /* gophertiles_022.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_022.jpg; sourceTree = "<group>"; };
+               B2E6FE251AC2EFA200D557D8 /* gophertiles_023.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_023.jpg; sourceTree = "<group>"; };
+               B2E6FE261AC2EFA200D557D8 /* gophertiles_024.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_024.jpg; sourceTree = "<group>"; };
+               B2E6FE271AC2EFA200D557D8 /* gophertiles_025.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_025.jpg; sourceTree = "<group>"; };
+               B2E6FE281AC2EFA200D557D8 /* gophertiles_026.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_026.jpg; sourceTree = "<group>"; };
+               B2E6FE291AC2EFA200D557D8 /* gophertiles_027.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_027.jpg; sourceTree = "<group>"; };
+               B2E6FE2A1AC2EFA200D557D8 /* gophertiles_028.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_028.jpg; sourceTree = "<group>"; };
+               B2E6FE2B1AC2EFA200D557D8 /* gophertiles_029.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_029.jpg; sourceTree = "<group>"; };
+               B2E6FE2C1AC2EFA200D557D8 /* gophertiles_030.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_030.jpg; sourceTree = "<group>"; };
+               B2E6FE2D1AC2EFA200D557D8 /* gophertiles_031.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_031.jpg; sourceTree = "<group>"; };
+               B2E6FE2E1AC2EFA200D557D8 /* gophertiles_032.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_032.jpg; sourceTree = "<group>"; };
+               B2E6FE2F1AC2EFA200D557D8 /* gophertiles_033.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_033.jpg; sourceTree = "<group>"; };
+               B2E6FE301AC2EFA200D557D8 /* gophertiles_034.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_034.jpg; sourceTree = "<group>"; };
+               B2E6FE311AC2EFA200D557D8 /* gophertiles_035.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_035.jpg; sourceTree = "<group>"; };
+               B2E6FE321AC2EFA200D557D8 /* gophertiles_036.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_036.jpg; sourceTree = "<group>"; };
+               B2E6FE331AC2EFA200D557D8 /* gophertiles_037.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_037.jpg; sourceTree = "<group>"; };
+               B2E6FE341AC2EFA200D557D8 /* gophertiles_038.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_038.jpg; sourceTree = "<group>"; };
+               B2E6FE351AC2EFA200D557D8 /* gophertiles_039.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_039.jpg; sourceTree = "<group>"; };
+               B2E6FE361AC2EFA200D557D8 /* gophertiles_040.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_040.jpg; sourceTree = "<group>"; };
+               B2E6FE371AC2EFA200D557D8 /* gophertiles_041.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_041.jpg; sourceTree = "<group>"; };
+               B2E6FE381AC2EFA200D557D8 /* gophertiles_042.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_042.jpg; sourceTree = "<group>"; };
+               B2E6FE391AC2EFA200D557D8 /* gophertiles_043.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_043.jpg; sourceTree = "<group>"; };
+               B2E6FE3A1AC2EFA200D557D8 /* gophertiles_044.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_044.jpg; sourceTree = "<group>"; };
+               B2E6FE3B1AC2EFA200D557D8 /* gophertiles_045.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_045.jpg; sourceTree = "<group>"; };
+               B2E6FE3C1AC2EFA200D557D8 /* gophertiles_046.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_046.jpg; sourceTree = "<group>"; };
+               B2E6FE3D1AC2EFA200D557D8 /* gophertiles_047.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_047.jpg; sourceTree = "<group>"; };
+               B2E6FE3E1AC2EFA200D557D8 /* gophertiles_048.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_048.jpg; sourceTree = "<group>"; };
+               B2E6FE3F1AC2EFA200D557D8 /* gophertiles_049.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_049.jpg; sourceTree = "<group>"; };
+               B2E6FE401AC2EFA200D557D8 /* gophertiles_050.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_050.jpg; sourceTree = "<group>"; };
+               B2E6FE411AC2EFA200D557D8 /* gophertiles_051.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_051.jpg; sourceTree = "<group>"; };
+               B2E6FE421AC2EFA200D557D8 /* gophertiles_052.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_052.jpg; sourceTree = "<group>"; };
+               B2E6FE431AC2EFA200D557D8 /* gophertiles_053.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_053.jpg; sourceTree = "<group>"; };
+               B2E6FE441AC2EFA200D557D8 /* gophertiles_054.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_054.jpg; sourceTree = "<group>"; };
+               B2E6FE451AC2EFA200D557D8 /* gophertiles_055.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_055.jpg; sourceTree = "<group>"; };
+               B2E6FE461AC2EFA200D557D8 /* gophertiles_056.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_056.jpg; sourceTree = "<group>"; };
+               B2E6FE471AC2EFA200D557D8 /* gophertiles_057.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_057.jpg; sourceTree = "<group>"; };
+               B2E6FE481AC2EFA200D557D8 /* gophertiles_058.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_058.jpg; sourceTree = "<group>"; };
+               B2E6FE491AC2EFA200D557D8 /* gophertiles_059.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_059.jpg; sourceTree = "<group>"; };
+               B2E6FE4A1AC2EFA200D557D8 /* gophertiles_060.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_060.jpg; sourceTree = "<group>"; };
+               B2E6FE4B1AC2EFA200D557D8 /* gophertiles_061.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_061.jpg; sourceTree = "<group>"; };
+               B2E6FE4C1AC2EFA200D557D8 /* gophertiles_062.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_062.jpg; sourceTree = "<group>"; };
+               B2E6FE4D1AC2EFA200D557D8 /* gophertiles_063.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_063.jpg; sourceTree = "<group>"; };
+               B2E6FE4E1AC2EFA200D557D8 /* gophertiles_064.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_064.jpg; sourceTree = "<group>"; };
+               B2E6FE4F1AC2EFA200D557D8 /* gophertiles_065.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_065.jpg; sourceTree = "<group>"; };
+               B2E6FE501AC2EFA200D557D8 /* gophertiles_066.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_066.jpg; sourceTree = "<group>"; };
+               B2E6FE511AC2EFA200D557D8 /* gophertiles_067.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_067.jpg; sourceTree = "<group>"; };
+               B2E6FE521AC2EFA200D557D8 /* gophertiles_068.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_068.jpg; sourceTree = "<group>"; };
+               B2E6FE531AC2EFA200D557D8 /* gophertiles_069.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_069.jpg; sourceTree = "<group>"; };
+               B2E6FE541AC2EFA200D557D8 /* gophertiles_070.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_070.jpg; sourceTree = "<group>"; };
+               B2E6FE551AC2EFA200D557D8 /* gophertiles_071.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_071.jpg; sourceTree = "<group>"; };
+               B2E6FE561AC2EFA200D557D8 /* gophertiles_072.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_072.jpg; sourceTree = "<group>"; };
+               B2E6FE571AC2EFA200D557D8 /* gophertiles_073.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_073.jpg; sourceTree = "<group>"; };
+               B2E6FE581AC2EFA200D557D8 /* gophertiles_074.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_074.jpg; sourceTree = "<group>"; };
+               B2E6FE591AC2EFA200D557D8 /* gophertiles_075.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_075.jpg; sourceTree = "<group>"; };
+               B2E6FE5A1AC2EFA200D557D8 /* gophertiles_076.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_076.jpg; sourceTree = "<group>"; };
+               B2E6FE5B1AC2EFA200D557D8 /* gophertiles_077.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_077.jpg; sourceTree = "<group>"; };
+               B2E6FE5C1AC2EFA200D557D8 /* gophertiles_078.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_078.jpg; sourceTree = "<group>"; };
+               B2E6FE5D1AC2EFA200D557D8 /* gophertiles_079.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_079.jpg; sourceTree = "<group>"; };
+               B2E6FE5E1AC2EFA200D557D8 /* gophertiles_080.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_080.jpg; sourceTree = "<group>"; };
+               B2E6FE5F1AC2EFA200D557D8 /* gophertiles_081.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_081.jpg; sourceTree = "<group>"; };
+               B2E6FE601AC2EFA200D557D8 /* gophertiles_082.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_082.jpg; sourceTree = "<group>"; };
+               B2E6FE611AC2EFA200D557D8 /* gophertiles_083.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_083.jpg; sourceTree = "<group>"; };
+               B2E6FE621AC2EFA200D557D8 /* gophertiles_084.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_084.jpg; sourceTree = "<group>"; };
+               B2E6FE631AC2EFA200D557D8 /* gophertiles_085.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_085.jpg; sourceTree = "<group>"; };
+               B2E6FE641AC2EFA200D557D8 /* gophertiles_086.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_086.jpg; sourceTree = "<group>"; };
+               B2E6FE651AC2EFA200D557D8 /* gophertiles_087.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_087.jpg; sourceTree = "<group>"; };
+               B2E6FE661AC2EFA200D557D8 /* gophertiles_088.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_088.jpg; sourceTree = "<group>"; };
+               B2E6FE671AC2EFA200D557D8 /* gophertiles_089.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_089.jpg; sourceTree = "<group>"; };
+               B2E6FE681AC2EFA200D557D8 /* gophertiles_090.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_090.jpg; sourceTree = "<group>"; };
+               B2E6FE691AC2EFA200D557D8 /* gophertiles_091.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_091.jpg; sourceTree = "<group>"; };
+               B2E6FE6A1AC2EFA200D557D8 /* gophertiles_092.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_092.jpg; sourceTree = "<group>"; };
+               B2E6FE6B1AC2EFA200D557D8 /* gophertiles_093.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_093.jpg; sourceTree = "<group>"; };
+               B2E6FE6C1AC2EFA200D557D8 /* gophertiles_094.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_094.jpg; sourceTree = "<group>"; };
+               B2E6FE6D1AC2EFA200D557D8 /* gophertiles_095.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_095.jpg; sourceTree = "<group>"; };
+               B2E6FE6E1AC2EFA200D557D8 /* gophertiles_096.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_096.jpg; sourceTree = "<group>"; };
+               B2E6FE6F1AC2EFA200D557D8 /* gophertiles_097.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_097.jpg; sourceTree = "<group>"; };
+               B2E6FE701AC2EFA200D557D8 /* gophertiles_098.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_098.jpg; sourceTree = "<group>"; };
+               B2E6FE711AC2EFA200D557D8 /* gophertiles_099.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_099.jpg; sourceTree = "<group>"; };
+               B2E6FE721AC2EFA200D557D8 /* gophertiles_100.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_100.jpg; sourceTree = "<group>"; };
+               B2E6FE731AC2EFA200D557D8 /* gophertiles_101.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_101.jpg; sourceTree = "<group>"; };
+               B2E6FE741AC2EFA200D557D8 /* gophertiles_102.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_102.jpg; sourceTree = "<group>"; };
+               B2E6FE751AC2EFA200D557D8 /* gophertiles_103.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_103.jpg; sourceTree = "<group>"; };
+               B2E6FE761AC2EFA200D557D8 /* gophertiles_104.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_104.jpg; sourceTree = "<group>"; };
+               B2E6FE771AC2EFA200D557D8 /* gophertiles_105.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_105.jpg; sourceTree = "<group>"; };
+               B2E6FE781AC2EFA200D557D8 /* gophertiles_106.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_106.jpg; sourceTree = "<group>"; };
+               B2E6FE791AC2EFA200D557D8 /* gophertiles_107.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_107.jpg; sourceTree = "<group>"; };
+               B2E6FE7A1AC2EFA200D557D8 /* gophertiles_108.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_108.jpg; sourceTree = "<group>"; };
+               B2E6FE7B1AC2EFA200D557D8 /* gophertiles_109.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_109.jpg; sourceTree = "<group>"; };
+               B2E6FE7C1AC2EFA200D557D8 /* gophertiles_110.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_110.jpg; sourceTree = "<group>"; };
+               B2E6FE7D1AC2EFA200D557D8 /* gophertiles_111.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_111.jpg; sourceTree = "<group>"; };
+               B2E6FE7E1AC2EFA200D557D8 /* gophertiles_112.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_112.jpg; sourceTree = "<group>"; };
+               B2E6FE7F1AC2EFA200D557D8 /* gophertiles_113.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_113.jpg; sourceTree = "<group>"; };
+               B2E6FE801AC2EFA200D557D8 /* gophertiles_114.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_114.jpg; sourceTree = "<group>"; };
+               B2E6FE811AC2EFA200D557D8 /* gophertiles_115.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_115.jpg; sourceTree = "<group>"; };
+               B2E6FE821AC2EFA200D557D8 /* gophertiles_116.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_116.jpg; sourceTree = "<group>"; };
+               B2E6FE831AC2EFA200D557D8 /* gophertiles_117.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_117.jpg; sourceTree = "<group>"; };
+               B2E6FE841AC2EFA200D557D8 /* gophertiles_118.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_118.jpg; sourceTree = "<group>"; };
+               B2E6FE851AC2EFA200D557D8 /* gophertiles_119.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_119.jpg; sourceTree = "<group>"; };
+               B2E6FE861AC2EFA200D557D8 /* gophertiles_120.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_120.jpg; sourceTree = "<group>"; };
+               B2E6FE871AC2EFA200D557D8 /* gophertiles_121.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_121.jpg; sourceTree = "<group>"; };
+               B2E6FE881AC2EFA200D557D8 /* gophertiles_122.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_122.jpg; sourceTree = "<group>"; };
+               B2E6FE891AC2EFA200D557D8 /* gophertiles_123.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_123.jpg; sourceTree = "<group>"; };
+               B2E6FE8A1AC2EFA200D557D8 /* gophertiles_124.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_124.jpg; sourceTree = "<group>"; };
+               B2E6FE8B1AC2EFA200D557D8 /* gophertiles_125.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_125.jpg; sourceTree = "<group>"; };
+               B2E6FE8C1AC2EFA200D557D8 /* gophertiles_126.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_126.jpg; sourceTree = "<group>"; };
+               B2E6FE8D1AC2EFA200D557D8 /* gophertiles_127.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_127.jpg; sourceTree = "<group>"; };
+               B2E6FE8E1AC2EFA200D557D8 /* gophertiles_128.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_128.jpg; sourceTree = "<group>"; };
+               B2E6FE8F1AC2EFA200D557D8 /* gophertiles_129.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_129.jpg; sourceTree = "<group>"; };
+               B2E6FE901AC2EFA200D557D8 /* gophertiles_130.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_130.jpg; sourceTree = "<group>"; };
+               B2E6FE911AC2EFA200D557D8 /* gophertiles_131.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_131.jpg; sourceTree = "<group>"; };
+               B2E6FE921AC2EFA200D557D8 /* gophertiles_132.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_132.jpg; sourceTree = "<group>"; };
+               B2E6FE931AC2EFA200D557D8 /* gophertiles_133.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_133.jpg; sourceTree = "<group>"; };
+               B2E6FE941AC2EFA200D557D8 /* gophertiles_134.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_134.jpg; sourceTree = "<group>"; };
+               B2E6FE951AC2EFA200D557D8 /* gophertiles_135.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_135.jpg; sourceTree = "<group>"; };
+               B2E6FE961AC2EFA200D557D8 /* gophertiles_136.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_136.jpg; sourceTree = "<group>"; };
+               B2E6FE971AC2EFA200D557D8 /* gophertiles_137.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_137.jpg; sourceTree = "<group>"; };
+               B2E6FE981AC2EFA200D557D8 /* gophertiles_138.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_138.jpg; sourceTree = "<group>"; };
+               B2E6FE991AC2EFA200D557D8 /* gophertiles_139.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_139.jpg; sourceTree = "<group>"; };
+               B2E6FE9A1AC2EFA200D557D8 /* gophertiles_140.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_140.jpg; sourceTree = "<group>"; };
+               B2E6FE9B1AC2EFA200D557D8 /* gophertiles_141.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_141.jpg; sourceTree = "<group>"; };
+               B2E6FE9C1AC2EFA200D557D8 /* gophertiles_142.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_142.jpg; sourceTree = "<group>"; };
+               B2E6FE9D1AC2EFA200D557D8 /* gophertiles_143.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_143.jpg; sourceTree = "<group>"; };
+               B2E6FE9E1AC2EFA200D557D8 /* gophertiles_144.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_144.jpg; sourceTree = "<group>"; };
+               B2E6FE9F1AC2EFA200D557D8 /* gophertiles_145.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_145.jpg; sourceTree = "<group>"; };
+               B2E6FEA01AC2EFA200D557D8 /* gophertiles_146.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_146.jpg; sourceTree = "<group>"; };
+               B2E6FEA11AC2EFA200D557D8 /* gophertiles_147.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_147.jpg; sourceTree = "<group>"; };
+               B2E6FEA21AC2EFA200D557D8 /* gophertiles_148.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_148.jpg; sourceTree = "<group>"; };
+               B2E6FEA31AC2EFA200D557D8 /* gophertiles_149.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_149.jpg; sourceTree = "<group>"; };
+               B2E6FEA41AC2EFA200D557D8 /* gophertiles_150.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_150.jpg; sourceTree = "<group>"; };
+               B2E6FEA51AC2EFA200D557D8 /* gophertiles_151.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_151.jpg; sourceTree = "<group>"; };
+               B2E6FEA61AC2EFA200D557D8 /* gophertiles_152.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_152.jpg; sourceTree = "<group>"; };
+               B2E6FEA71AC2EFA200D557D8 /* gophertiles_153.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_153.jpg; sourceTree = "<group>"; };
+               B2E6FEA81AC2EFA200D557D8 /* gophertiles_154.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_154.jpg; sourceTree = "<group>"; };
+               B2E6FEA91AC2EFA200D557D8 /* gophertiles_155.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_155.jpg; sourceTree = "<group>"; };
+               B2E6FEAA1AC2EFA200D557D8 /* gophertiles_156.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_156.jpg; sourceTree = "<group>"; };
+               B2E6FEAB1AC2EFA200D557D8 /* gophertiles_157.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_157.jpg; sourceTree = "<group>"; };
+               B2E6FEAC1AC2EFA200D557D8 /* gophertiles_158.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_158.jpg; sourceTree = "<group>"; };
+               B2E6FEAD1AC2EFA200D557D8 /* gophertiles_159.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_159.jpg; sourceTree = "<group>"; };
+               B2E6FEAE1AC2EFA200D557D8 /* gophertiles_160.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_160.jpg; sourceTree = "<group>"; };
+               B2E6FEAF1AC2EFA200D557D8 /* gophertiles_161.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_161.jpg; sourceTree = "<group>"; };
+               B2E6FEB01AC2EFA200D557D8 /* gophertiles_162.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_162.jpg; sourceTree = "<group>"; };
+               B2E6FEB11AC2EFA200D557D8 /* gophertiles_163.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_163.jpg; sourceTree = "<group>"; };
+               B2E6FEB21AC2EFA200D557D8 /* gophertiles_164.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_164.jpg; sourceTree = "<group>"; };
+               B2E6FEB31AC2EFA200D557D8 /* gophertiles_165.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_165.jpg; sourceTree = "<group>"; };
+               B2E6FEB41AC2EFA200D557D8 /* gophertiles_166.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_166.jpg; sourceTree = "<group>"; };
+               B2E6FEB51AC2EFA300D557D8 /* gophertiles_167.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_167.jpg; sourceTree = "<group>"; };
+               B2E6FEB61AC2EFA300D557D8 /* gophertiles_168.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_168.jpg; sourceTree = "<group>"; };
+               B2E6FEB71AC2EFA300D557D8 /* gophertiles_169.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_169.jpg; sourceTree = "<group>"; };
+               B2E6FEB81AC2EFA300D557D8 /* gophertiles_170.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_170.jpg; sourceTree = "<group>"; };
+               B2E6FEB91AC2EFA300D557D8 /* gophertiles_171.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_171.jpg; sourceTree = "<group>"; };
+               B2E6FEBA1AC2EFA300D557D8 /* gophertiles_172.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_172.jpg; sourceTree = "<group>"; };
+               B2E6FEBB1AC2EFA300D557D8 /* gophertiles_173.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_173.jpg; sourceTree = "<group>"; };
+               B2E6FEBC1AC2EFA300D557D8 /* gophertiles_174.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_174.jpg; sourceTree = "<group>"; };
+               B2E6FEBD1AC2EFA300D557D8 /* gophertiles_175.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_175.jpg; sourceTree = "<group>"; };
+               B2E6FEBE1AC2EFA300D557D8 /* gophertiles_176.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_176.jpg; sourceTree = "<group>"; };
+               B2E6FEBF1AC2EFA300D557D8 /* gophertiles_177.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_177.jpg; sourceTree = "<group>"; };
+               B2E6FEC01AC2EFA300D557D8 /* gophertiles_178.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_178.jpg; sourceTree = "<group>"; };
+               B2E6FEC11AC2EFA300D557D8 /* gophertiles_179.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_179.jpg; sourceTree = "<group>"; };
+               B2E6FEC21AC2EFA300D557D8 /* gophertiles_180.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = gophertiles_180.jpg; sourceTree = "<group>"; };
+               B2E6FEC31AC2EFA300D557D8 /* 004.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = 004.html; sourceTree = "<group>"; };
+               B2E6FEC41AC2EFA300D557D8 /* 005.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 005.txt; sourceTree = "<group>"; };
+               B2E6FEC61AC2EFA300D557D8 /* 006.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = 006.css; sourceTree = "<group>"; };
+               B2E6FEC71AC2EFA300D557D8 /* 006.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = 006.js; sourceTree = "<group>"; };
+               B2E6FEC81AC2EFA300D557D8 /* 006.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = 006.html; sourceTree = "<group>"; };
+               B2E6FECA1AC2EFA300D557D8 /* 007.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = 007.py; sourceTree = "<group>"; };
+               B2E6FECB1AC2EFA300D557D8 /* 007.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = 007.html; sourceTree = "<group>"; };
+               B2E6FECF1AC2EFA300D557D8 /* 009.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = 009.py; sourceTree = "<group>"; };
+               B2E6FED11AC2EFA300D557D8 /* empty.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = empty.txt; sourceTree = "<group>"; };
+               B2E6FED21AC2EFA300D557D8 /* hello.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = hello.py; sourceTree = "<group>"; };
+               B2E6FED31AC2EFA300D557D8 /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = "<group>"; };
+               B2E6FED41AC2EFA300D557D8 /* necho.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = necho.py; sourceTree = "<group>"; };
+               B2E6FED51AC2EFA300D557D8 /* upload.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = upload.py; sourceTree = "<group>"; };
+               B2E6FED61AC2EFA300D557D8 /* xxx-1.0.2a.tar.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = "xxx-1.0.2a.tar.gz"; sourceTree = "<group>"; };
+               B2E6FED71AC2EFA300D557D8 /* load-urls-1.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "load-urls-1.txt"; sourceTree = "<group>"; };
+               B2E6FED81AC2EFA300D557D8 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+               B2E6FED91AC2EFA300D557D8 /* test_nghttp_get.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = test_nghttp_get.sh; sourceTree = "<group>"; };
+               B2E6FEDA1AC2EFA300D557D8 /* test_nghttp_post.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = test_nghttp_post.sh; sourceTree = "<group>"; };
+               B2E6FEDB1AC2EFA300D557D8 /* test_common.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = test_common.sh; sourceTree = "<group>"; };
+               B2E6FEDC1AC2F01F00D557D8 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+               B2E6FEDD1AC2F20400D557D8 /* h2.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = h2.conf; sourceTree = "<group>"; };
+               B2E6FEDE1AC2F20400D557D8 /* h2.load */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = h2.load; sourceTree = "<group>"; };
+               B2E708C81AE64AC00009EDAF /* php-wrapper */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "php-wrapper"; sourceTree = "<group>"; };
+               B2E708C91AE64B470009EDAF /* info.php */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.php; path = info.php; sourceTree = "<group>"; };
+               B2FE998E1AA7496B00A5CE5A /* h2_to_h1.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = h2_to_h1.c; sourceTree = "<group>"; };
+               B2FE998F1AA7496B00A5CE5A /* h2_to_h1.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_to_h1.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXGroup section */
+               B225490A1A6EAAA3004BDEC9 /* mod_h2 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B20946CC1B2865BB0041992F /* h2_alpn.c */,
+                               B20946CD1B2865BB0041992F /* h2_alpn.h */,
+                               B24CB1221AC94D6A0057413B /* h2_alt_svc.c */,
+                               B24CB1231AC94D6A0057413B /* h2_alt_svc.h */,
+                               B225490D1A6EAE9F004BDEC9 /* h2_config.c */,
+                               B225490E1A6EAE9F004BDEC9 /* h2_config.h */,
+                               B225493A1A711985004BDEC9 /* h2_conn_io.c */,
+                               B225493B1A711985004BDEC9 /* h2_conn_io.h */,
+                               B2CB610E1A88BB9F00D270A6 /* h2_conn.c */,
+                               B2CB610F1A88BB9F00D270A6 /* h2_conn.h */,
+                               B22549331A6FBEF1004BDEC9 /* h2_ctx.c */,
+                               B22549341A6FBEF1004BDEC9 /* h2_ctx.h */,
+                               B2546D971A7FC2A60033A875 /* h2_from_h1.c */,
+                               B2546D981A7FC2A60033A875 /* h2_from_h1.h */,
+                               B225491E1A6FB8DF004BDEC9 /* h2_h2.c */,
+                               B225491F1A6FB8DF004BDEC9 /* h2_h2.h */,
+                               B271425E1AB9DD59004EF82E /* h2_io_set.c */,
+                               B271425F1AB9DD59004EF82E /* h2_io_set.h */,
+                               B27142601AB9DD76004EF82E /* h2_io.c */,
+                               B27142611AB9DD76004EF82E /* h2_io.h */,
+                               B27D32E81A9487B4003DBAF4 /* h2_mplx.c */,
+                               B27D32E91A9487B4003DBAF4 /* h2_mplx.h */,
+                               B22549371A6FCE13004BDEC9 /* h2_private.h */,
+                               B2A6EF171A9B598B005DFC5B /* h2_request.c */,
+                               B2A6EF181A9B598B005DFC5B /* h2_request.h */,
+                               B2CB61101A8A0FB400D270A6 /* h2_response.c */,
+                               B2CB61111A8A0FB400D270A6 /* h2_response.h */,
+                               B22549381A710329004BDEC9 /* h2_session.c */,
+                               B22549391A710329004BDEC9 /* h2_session.h */,
+                               B2546D991A81079D0033A875 /* h2_stream_set.c */,
+                               B2546D9A1A81079D0033A875 /* h2_stream_set.h */,
+                               B2C16D251A779C55000B2297 /* h2_stream.c */,
+                               B2C16D261A779C55000B2297 /* h2_stream.h */,
+                               B284ACC91A7B9DF900C35863 /* h2_task_input.c */,
+                               B284ACCA1A7B9DF900C35863 /* h2_task_input.h */,
+                               B2546D8E1A7FA7D70033A875 /* h2_task_output.c */,
+                               B2546D8F1A7FA7D70033A875 /* h2_task_output.h */,
+                               B21BFCF91B023B840031EBCD /* h2_task_queue.c */,
+                               B21BFCFA1B023B840031EBCD /* h2_task_queue.h */,
+                               B2546D881A7F71F80033A875 /* h2_task.c */,
+                               B2546D891A7F71F80033A875 /* h2_task.h */,
+                               B2FE998E1AA7496B00A5CE5A /* h2_to_h1.c */,
+                               B2FE998F1AA7496B00A5CE5A /* h2_to_h1.h */,
+                               B2A6EF281A9C79A6005DFC5B /* h2_upgrade.c */,
+                               B2A6EF291A9C79A6005DFC5B /* h2_upgrade.h */,
+                               B225493C1A713D18004BDEC9 /* h2_util.c */,
+                               B225493D1A713D18004BDEC9 /* h2_util.h */,
+                               B2AABE611AC5A9B400A90B72 /* h2_version.h.in */,
+                               B2CB61141A8B698A00D270A6 /* h2_worker.c */,
+                               B2CB61151A8B698A00D270A6 /* h2_worker.h */,
+                               B2CB61161A8B699E00D270A6 /* h2_workers.c */,
+                               B2CB61171A8B699E00D270A6 /* h2_workers.h */,
+                               B2E5D5081AADAD28001FD280 /* Makefile.am */,
+                               B225490C1A6EAC37004BDEC9 /* mod_h2.c */,
+                               B22549361A6FCCAA004BDEC9 /* mod_h2.h */,
+                       );
+                       path = mod_h2;
+                       sourceTree = "<group>";
+               };
+               B27BBD1F1A65757700C58A41 = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E5D7861AAF1F4D001FD280 /* AUTHORS */,
+                               B2E5D7871AAF1F4D001FD280 /* ChangeLog */,
+                               B2E5D7841AAEEF8C001FD280 /* configure.ac */,
+                               B2E5D7881AAF1F4D001FD280 /* COPYING */,
+                               B2A6EF2C1A9F2452005DFC5B /* DISCUSS */,
+                               B2A6EF2A1A9E3A93005DFC5B /* INSTALL */,
+                               B22D25281AA89A0B0041D8E5 /* LICENSE */,
+                               B2E5D7851AAF1D87001FD280 /* Makefile.am */,
+                               B225490A1A6EAAA3004BDEC9 /* mod_h2 */,
+                               B2E5D7891AAF1F4D001FD280 /* NEWS */,
+                               B27BBD2D1A6575C200C58A41 /* README */,
+                               B256C4BA1ADD5FF10042C760 /* README.md */,
+                               B2E6DF791AC2EF6E00D557D8 /* sandbox */,
+                               B2E6DF761AC2E55700D557D8 /* setup */,
+                       );
+                       sourceTree = "<group>";
+               };
+               B2C63B0F1B3BF76B00127D1E /* mods-available */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2C63B121B3BF7CC00127D1E /* mpm_event.load */,
+                               B2C63B131B3BF7CC00127D1E /* mpm_prefork.load */,
+                               B2C63B101B3BF78C00127D1E /* mpm_worker.load */,
+                       );
+                       path = "mods-available";
+                       sourceTree = "<group>";
+               };
+               B2E6DF761AC2E55700D557D8 /* setup */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FEDD1AC2F20400D557D8 /* h2.conf */,
+                               B2E6FEDE1AC2F20400D557D8 /* h2.load */,
+                               B2E6DF771AC2E55800D557D8 /* Makefile.am */,
+                               B2E6DF781AC2E6A700D557D8 /* install-config.sh */,
+                       );
+                       path = setup;
+                       sourceTree = "<group>";
+               };
+               B2E6DF791AC2EF6E00D557D8 /* sandbox */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6DF7A1AC2EF6E00D557D8 /* httpd */,
+                               B2E6FEDC1AC2F01F00D557D8 /* Makefile.am */,
+                               B2E6DFA21AC2EF6E00D557D8 /* nghttp2 */,
+                               B2E6DFA61AC2EF6E00D557D8 /* test */,
+                       );
+                       path = sandbox;
+                       sourceTree = "<group>";
+               };
+               B2E6DF7A1AC2EF6E00D557D8 /* httpd */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6DF7B1AC2EF6E00D557D8 /* .gitignore */,
+                               B2E6DF7C1AC2EF6E00D557D8 /* get-openssl-latest.sh */,
+                               B2E6DF7D1AC2EF6E00D557D8 /* Makefile */,
+                               B2E6DF9C1AC2EF6E00D557D8 /* packages */,
+                               B2E6DF9F1AC2EF6E00D557D8 /* patches */,
+                       );
+                       path = httpd;
+                       sourceTree = "<group>";
+               };
+               B2E6DF9C1AC2EF6E00D557D8 /* packages */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6DF9E1AC2EF6E00D557D8 /* pcre-8.36.tar.gz */,
+                       );
+                       path = packages;
+                       sourceTree = "<group>";
+               };
+               B2E6DF9F1AC2EF6E00D557D8 /* patches */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2B170B91B31BF7000EDC007 /* httpd-2.4.12-alpn-v5.patch */,
+                               B272F2311B1E13D8007A20A5 /* httpd-2.4.x-alpn-v4.patch */,
+                               B2C6310C1B393CA100127D1E /* httpd-alpn-v4-v5.patch */,
+                               B227966E1AEE93B000376B40 /* httpd-npn.unified.diff.patch */,
+                               B2C63B0E1B39421B00127D1E /* openssl-1.0.2-alpn.patch */,
+                               B20946CE1B29D8C30041992F /* sni_misdirect.patch */,
+                       );
+                       path = patches;
+                       sourceTree = "<group>";
+               };
+               B2E6DFA21AC2EF6E00D557D8 /* nghttp2 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6DFA31AC2EF6E00D557D8 /* Makefile */,
+                               B2E6DFA41AC2EF6E00D557D8 /* patches */,
+                       );
+                       path = nghttp2;
+                       sourceTree = "<group>";
+               };
+               B2E6DFA41AC2EF6E00D557D8 /* patches */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       path = patches;
+                       sourceTree = "<group>";
+               };
+               B2E6DFA61AC2EF6E00D557D8 /* test */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E708C71AE64AC00009EDAF /* bin */,
+                               B2E6DFA71AC2EF6E00D557D8 /* clients */,
+                               B2E6FDF91AC2EFA200D557D8 /* conf */,
+                               B2E6FE071AC2EFA200D557D8 /* htdocs */,
+                               B2E6FED71AC2EFA300D557D8 /* load-urls-1.txt */,
+                               B2E6FED81AC2EFA300D557D8 /* Makefile */,
+                               B2E6FEDB1AC2EFA300D557D8 /* test_common.sh */,
+                               B24CB1241AC96BFE0057413B /* test_curl_altsvc.sh */,
+                               B20E441F1ACC07E8003D21AE /* test_curl_get.sh */,
+                               B20E44201ACC088E003D21AE /* test_curl_post.sh */,
+                               B2E6FED91AC2EFA300D557D8 /* test_nghttp_get.sh */,
+                               B2E6FEDA1AC2EFA300D557D8 /* test_nghttp_post.sh */,
+                               B20946CA1B25D4430041992F /* test_alt_host.sh */,
+                       );
+                       path = test;
+                       sourceTree = "<group>";
+               };
+               B2E6DFA71AC2EF6E00D557D8 /* clients */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FDF81AC2EFA200D557D8 /* Makefile */,
+                       );
+                       path = clients;
+                       sourceTree = "<group>";
+               };
+               B2E6FDF91AC2EFA200D557D8 /* conf */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FDFA1AC2EFA200D557D8 /* httpd.conf */,
+                               B2C63B0F1B3BF76B00127D1E /* mods-available */,
+                               B2E6FDFB1AC2EFA200D557D8 /* modules.conf */,
+                               B2E6FDFC1AC2EFA200D557D8 /* sites */,
+                               B2E6FDFF1AC2EFA200D557D8 /* ssl */,
+                       );
+                       path = conf;
+                       sourceTree = "<group>";
+               };
+               B2E6FDFC1AC2EFA200D557D8 /* sites */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2B170B81B316FB100EDC007 /* aaa-noh2.example.org.conf */,
+                               B2B170B31B2EC73400EDC007 /* test.example.org.conf */,
+                               B2E6FDFD1AC2EFA200D557D8 /* mod-h2.greenbytes.de.conf */,
+                               B2C631081B383EA800127D1E /* test-ser.example.org.conf */,
+                       );
+                       path = sites;
+                       sourceTree = "<group>";
+               };
+               B2E6FDFF1AC2EFA200D557D8 /* ssl */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FE001AC2EFA200D557D8 /* .gitignore */,
+                               B2E6FE011AC2EFA200D557D8 /* ca.pem */,
+                               B2E6FE021AC2EFA200D557D8 /* extensions.conf */,
+                               B2E6FE031AC2EFA200D557D8 /* mod-h2.greenbytes.de.key */,
+                               B2E6FE041AC2EFA200D557D8 /* mod-h2.greenbytes.de.pem */,
+                               B2E6FE051AC2EFA200D557D8 /* test.example.org.x509.input */,
+                               B2B170B61B303B0C00EDC007 /* noh2.example.org.x509.input */,
+                       );
+                       path = ssl;
+                       sourceTree = "<group>";
+               };
+               B2E6FE071AC2EFA200D557D8 /* htdocs */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FE081AC2EFA200D557D8 /* test.example.org */,
+                       );
+                       path = htdocs;
+                       sourceTree = "<group>";
+               };
+               B2E6FE081AC2EFA200D557D8 /* test.example.org */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FE091AC2EFA200D557D8 /* 001.html */,
+                               B2E6FE0A1AC2EFA200D557D8 /* 002.jpg */,
+                               B2E6FE0B1AC2EFA200D557D8 /* 003 */,
+                               B2E6FE0D1AC2EFA200D557D8 /* 003.html */,
+                               B2E6FE0E1AC2EFA200D557D8 /* 004 */,
+                               B2E6FEC31AC2EFA300D557D8 /* 004.html */,
+                               B2E6FEC41AC2EFA300D557D8 /* 005.txt */,
+                               B2E6FEC51AC2EFA300D557D8 /* 006 */,
+                               B2E6FEC81AC2EFA300D557D8 /* 006.html */,
+                               B2E6FEC91AC2EFA300D557D8 /* 007 */,
+                               B2E6FECB1AC2EFA300D557D8 /* 007.html */,
+                               B2E6FECF1AC2EFA300D557D8 /* 009.py */,
+                               B2E6FED01AC2EFA300D557D8 /* files */,
+                               B2E6FED21AC2EFA300D557D8 /* hello.py */,
+                               B2E6FED31AC2EFA300D557D8 /* index.html */,
+                               B2E708C91AE64B470009EDAF /* info.php */,
+                               B2E6FED41AC2EFA300D557D8 /* necho.py */,
+                               B2E6FED51AC2EFA300D557D8 /* upload.py */,
+                               B2E6FED61AC2EFA300D557D8 /* xxx-1.0.2a.tar.gz */,
+                       );
+                       path = test.example.org;
+                       sourceTree = "<group>";
+               };
+               B2E6FE0B1AC2EFA200D557D8 /* 003 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FE0C1AC2EFA200D557D8 /* 003_img.jpg */,
+                       );
+                       path = 003;
+                       sourceTree = "<group>";
+               };
+               B2E6FE0E1AC2EFA200D557D8 /* 004 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FE0F1AC2EFA200D557D8 /* gophertiles.jpg */,
+                               B2E6FE101AC2EFA200D557D8 /* gophertiles_002.jpg */,
+                               B2E6FE111AC2EFA200D557D8 /* gophertiles_003.jpg */,
+                               B2E6FE121AC2EFA200D557D8 /* gophertiles_004.jpg */,
+                               B2E6FE131AC2EFA200D557D8 /* gophertiles_005.jpg */,
+                               B2E6FE141AC2EFA200D557D8 /* gophertiles_006.jpg */,
+                               B2E6FE151AC2EFA200D557D8 /* gophertiles_007.jpg */,
+                               B2E6FE161AC2EFA200D557D8 /* gophertiles_008.jpg */,
+                               B2E6FE171AC2EFA200D557D8 /* gophertiles_009.jpg */,
+                               B2E6FE181AC2EFA200D557D8 /* gophertiles_010.jpg */,
+                               B2E6FE191AC2EFA200D557D8 /* gophertiles_011.jpg */,
+                               B2E6FE1A1AC2EFA200D557D8 /* gophertiles_012.jpg */,
+                               B2E6FE1B1AC2EFA200D557D8 /* gophertiles_013.jpg */,
+                               B2E6FE1C1AC2EFA200D557D8 /* gophertiles_014.jpg */,
+                               B2E6FE1D1AC2EFA200D557D8 /* gophertiles_015.jpg */,
+                               B2E6FE1E1AC2EFA200D557D8 /* gophertiles_016.jpg */,
+                               B2E6FE1F1AC2EFA200D557D8 /* gophertiles_017.jpg */,
+                               B2E6FE201AC2EFA200D557D8 /* gophertiles_018.jpg */,
+                               B2E6FE211AC2EFA200D557D8 /* gophertiles_019.jpg */,
+                               B2E6FE221AC2EFA200D557D8 /* gophertiles_020.jpg */,
+                               B2E6FE231AC2EFA200D557D8 /* gophertiles_021.jpg */,
+                               B2E6FE241AC2EFA200D557D8 /* gophertiles_022.jpg */,
+                               B2E6FE251AC2EFA200D557D8 /* gophertiles_023.jpg */,
+                               B2E6FE261AC2EFA200D557D8 /* gophertiles_024.jpg */,
+                               B2E6FE271AC2EFA200D557D8 /* gophertiles_025.jpg */,
+                               B2E6FE281AC2EFA200D557D8 /* gophertiles_026.jpg */,
+                               B2E6FE291AC2EFA200D557D8 /* gophertiles_027.jpg */,
+                               B2E6FE2A1AC2EFA200D557D8 /* gophertiles_028.jpg */,
+                               B2E6FE2B1AC2EFA200D557D8 /* gophertiles_029.jpg */,
+                               B2E6FE2C1AC2EFA200D557D8 /* gophertiles_030.jpg */,
+                               B2E6FE2D1AC2EFA200D557D8 /* gophertiles_031.jpg */,
+                               B2E6FE2E1AC2EFA200D557D8 /* gophertiles_032.jpg */,
+                               B2E6FE2F1AC2EFA200D557D8 /* gophertiles_033.jpg */,
+                               B2E6FE301AC2EFA200D557D8 /* gophertiles_034.jpg */,
+                               B2E6FE311AC2EFA200D557D8 /* gophertiles_035.jpg */,
+                               B2E6FE321AC2EFA200D557D8 /* gophertiles_036.jpg */,
+                               B2E6FE331AC2EFA200D557D8 /* gophertiles_037.jpg */,
+                               B2E6FE341AC2EFA200D557D8 /* gophertiles_038.jpg */,
+                               B2E6FE351AC2EFA200D557D8 /* gophertiles_039.jpg */,
+                               B2E6FE361AC2EFA200D557D8 /* gophertiles_040.jpg */,
+                               B2E6FE371AC2EFA200D557D8 /* gophertiles_041.jpg */,
+                               B2E6FE381AC2EFA200D557D8 /* gophertiles_042.jpg */,
+                               B2E6FE391AC2EFA200D557D8 /* gophertiles_043.jpg */,
+                               B2E6FE3A1AC2EFA200D557D8 /* gophertiles_044.jpg */,
+                               B2E6FE3B1AC2EFA200D557D8 /* gophertiles_045.jpg */,
+                               B2E6FE3C1AC2EFA200D557D8 /* gophertiles_046.jpg */,
+                               B2E6FE3D1AC2EFA200D557D8 /* gophertiles_047.jpg */,
+                               B2E6FE3E1AC2EFA200D557D8 /* gophertiles_048.jpg */,
+                               B2E6FE3F1AC2EFA200D557D8 /* gophertiles_049.jpg */,
+                               B2E6FE401AC2EFA200D557D8 /* gophertiles_050.jpg */,
+                               B2E6FE411AC2EFA200D557D8 /* gophertiles_051.jpg */,
+                               B2E6FE421AC2EFA200D557D8 /* gophertiles_052.jpg */,
+                               B2E6FE431AC2EFA200D557D8 /* gophertiles_053.jpg */,
+                               B2E6FE441AC2EFA200D557D8 /* gophertiles_054.jpg */,
+                               B2E6FE451AC2EFA200D557D8 /* gophertiles_055.jpg */,
+                               B2E6FE461AC2EFA200D557D8 /* gophertiles_056.jpg */,
+                               B2E6FE471AC2EFA200D557D8 /* gophertiles_057.jpg */,
+                               B2E6FE481AC2EFA200D557D8 /* gophertiles_058.jpg */,
+                               B2E6FE491AC2EFA200D557D8 /* gophertiles_059.jpg */,
+                               B2E6FE4A1AC2EFA200D557D8 /* gophertiles_060.jpg */,
+                               B2E6FE4B1AC2EFA200D557D8 /* gophertiles_061.jpg */,
+                               B2E6FE4C1AC2EFA200D557D8 /* gophertiles_062.jpg */,
+                               B2E6FE4D1AC2EFA200D557D8 /* gophertiles_063.jpg */,
+                               B2E6FE4E1AC2EFA200D557D8 /* gophertiles_064.jpg */,
+                               B2E6FE4F1AC2EFA200D557D8 /* gophertiles_065.jpg */,
+                               B2E6FE501AC2EFA200D557D8 /* gophertiles_066.jpg */,
+                               B2E6FE511AC2EFA200D557D8 /* gophertiles_067.jpg */,
+                               B2E6FE521AC2EFA200D557D8 /* gophertiles_068.jpg */,
+                               B2E6FE531AC2EFA200D557D8 /* gophertiles_069.jpg */,
+                               B2E6FE541AC2EFA200D557D8 /* gophertiles_070.jpg */,
+                               B2E6FE551AC2EFA200D557D8 /* gophertiles_071.jpg */,
+                               B2E6FE561AC2EFA200D557D8 /* gophertiles_072.jpg */,
+                               B2E6FE571AC2EFA200D557D8 /* gophertiles_073.jpg */,
+                               B2E6FE581AC2EFA200D557D8 /* gophertiles_074.jpg */,
+                               B2E6FE591AC2EFA200D557D8 /* gophertiles_075.jpg */,
+                               B2E6FE5A1AC2EFA200D557D8 /* gophertiles_076.jpg */,
+                               B2E6FE5B1AC2EFA200D557D8 /* gophertiles_077.jpg */,
+                               B2E6FE5C1AC2EFA200D557D8 /* gophertiles_078.jpg */,
+                               B2E6FE5D1AC2EFA200D557D8 /* gophertiles_079.jpg */,
+                               B2E6FE5E1AC2EFA200D557D8 /* gophertiles_080.jpg */,
+                               B2E6FE5F1AC2EFA200D557D8 /* gophertiles_081.jpg */,
+                               B2E6FE601AC2EFA200D557D8 /* gophertiles_082.jpg */,
+                               B2E6FE611AC2EFA200D557D8 /* gophertiles_083.jpg */,
+                               B2E6FE621AC2EFA200D557D8 /* gophertiles_084.jpg */,
+                               B2E6FE631AC2EFA200D557D8 /* gophertiles_085.jpg */,
+                               B2E6FE641AC2EFA200D557D8 /* gophertiles_086.jpg */,
+                               B2E6FE651AC2EFA200D557D8 /* gophertiles_087.jpg */,
+                               B2E6FE661AC2EFA200D557D8 /* gophertiles_088.jpg */,
+                               B2E6FE671AC2EFA200D557D8 /* gophertiles_089.jpg */,
+                               B2E6FE681AC2EFA200D557D8 /* gophertiles_090.jpg */,
+                               B2E6FE691AC2EFA200D557D8 /* gophertiles_091.jpg */,
+                               B2E6FE6A1AC2EFA200D557D8 /* gophertiles_092.jpg */,
+                               B2E6FE6B1AC2EFA200D557D8 /* gophertiles_093.jpg */,
+                               B2E6FE6C1AC2EFA200D557D8 /* gophertiles_094.jpg */,
+                               B2E6FE6D1AC2EFA200D557D8 /* gophertiles_095.jpg */,
+                               B2E6FE6E1AC2EFA200D557D8 /* gophertiles_096.jpg */,
+                               B2E6FE6F1AC2EFA200D557D8 /* gophertiles_097.jpg */,
+                               B2E6FE701AC2EFA200D557D8 /* gophertiles_098.jpg */,
+                               B2E6FE711AC2EFA200D557D8 /* gophertiles_099.jpg */,
+                               B2E6FE721AC2EFA200D557D8 /* gophertiles_100.jpg */,
+                               B2E6FE731AC2EFA200D557D8 /* gophertiles_101.jpg */,
+                               B2E6FE741AC2EFA200D557D8 /* gophertiles_102.jpg */,
+                               B2E6FE751AC2EFA200D557D8 /* gophertiles_103.jpg */,
+                               B2E6FE761AC2EFA200D557D8 /* gophertiles_104.jpg */,
+                               B2E6FE771AC2EFA200D557D8 /* gophertiles_105.jpg */,
+                               B2E6FE781AC2EFA200D557D8 /* gophertiles_106.jpg */,
+                               B2E6FE791AC2EFA200D557D8 /* gophertiles_107.jpg */,
+                               B2E6FE7A1AC2EFA200D557D8 /* gophertiles_108.jpg */,
+                               B2E6FE7B1AC2EFA200D557D8 /* gophertiles_109.jpg */,
+                               B2E6FE7C1AC2EFA200D557D8 /* gophertiles_110.jpg */,
+                               B2E6FE7D1AC2EFA200D557D8 /* gophertiles_111.jpg */,
+                               B2E6FE7E1AC2EFA200D557D8 /* gophertiles_112.jpg */,
+                               B2E6FE7F1AC2EFA200D557D8 /* gophertiles_113.jpg */,
+                               B2E6FE801AC2EFA200D557D8 /* gophertiles_114.jpg */,
+                               B2E6FE811AC2EFA200D557D8 /* gophertiles_115.jpg */,
+                               B2E6FE821AC2EFA200D557D8 /* gophertiles_116.jpg */,
+                               B2E6FE831AC2EFA200D557D8 /* gophertiles_117.jpg */,
+                               B2E6FE841AC2EFA200D557D8 /* gophertiles_118.jpg */,
+                               B2E6FE851AC2EFA200D557D8 /* gophertiles_119.jpg */,
+                               B2E6FE861AC2EFA200D557D8 /* gophertiles_120.jpg */,
+                               B2E6FE871AC2EFA200D557D8 /* gophertiles_121.jpg */,
+                               B2E6FE881AC2EFA200D557D8 /* gophertiles_122.jpg */,
+                               B2E6FE891AC2EFA200D557D8 /* gophertiles_123.jpg */,
+                               B2E6FE8A1AC2EFA200D557D8 /* gophertiles_124.jpg */,
+                               B2E6FE8B1AC2EFA200D557D8 /* gophertiles_125.jpg */,
+                               B2E6FE8C1AC2EFA200D557D8 /* gophertiles_126.jpg */,
+                               B2E6FE8D1AC2EFA200D557D8 /* gophertiles_127.jpg */,
+                               B2E6FE8E1AC2EFA200D557D8 /* gophertiles_128.jpg */,
+                               B2E6FE8F1AC2EFA200D557D8 /* gophertiles_129.jpg */,
+                               B2E6FE901AC2EFA200D557D8 /* gophertiles_130.jpg */,
+                               B2E6FE911AC2EFA200D557D8 /* gophertiles_131.jpg */,
+                               B2E6FE921AC2EFA200D557D8 /* gophertiles_132.jpg */,
+                               B2E6FE931AC2EFA200D557D8 /* gophertiles_133.jpg */,
+                               B2E6FE941AC2EFA200D557D8 /* gophertiles_134.jpg */,
+                               B2E6FE951AC2EFA200D557D8 /* gophertiles_135.jpg */,
+                               B2E6FE961AC2EFA200D557D8 /* gophertiles_136.jpg */,
+                               B2E6FE971AC2EFA200D557D8 /* gophertiles_137.jpg */,
+                               B2E6FE981AC2EFA200D557D8 /* gophertiles_138.jpg */,
+                               B2E6FE991AC2EFA200D557D8 /* gophertiles_139.jpg */,
+                               B2E6FE9A1AC2EFA200D557D8 /* gophertiles_140.jpg */,
+                               B2E6FE9B1AC2EFA200D557D8 /* gophertiles_141.jpg */,
+                               B2E6FE9C1AC2EFA200D557D8 /* gophertiles_142.jpg */,
+                               B2E6FE9D1AC2EFA200D557D8 /* gophertiles_143.jpg */,
+                               B2E6FE9E1AC2EFA200D557D8 /* gophertiles_144.jpg */,
+                               B2E6FE9F1AC2EFA200D557D8 /* gophertiles_145.jpg */,
+                               B2E6FEA01AC2EFA200D557D8 /* gophertiles_146.jpg */,
+                               B2E6FEA11AC2EFA200D557D8 /* gophertiles_147.jpg */,
+                               B2E6FEA21AC2EFA200D557D8 /* gophertiles_148.jpg */,
+                               B2E6FEA31AC2EFA200D557D8 /* gophertiles_149.jpg */,
+                               B2E6FEA41AC2EFA200D557D8 /* gophertiles_150.jpg */,
+                               B2E6FEA51AC2EFA200D557D8 /* gophertiles_151.jpg */,
+                               B2E6FEA61AC2EFA200D557D8 /* gophertiles_152.jpg */,
+                               B2E6FEA71AC2EFA200D557D8 /* gophertiles_153.jpg */,
+                               B2E6FEA81AC2EFA200D557D8 /* gophertiles_154.jpg */,
+                               B2E6FEA91AC2EFA200D557D8 /* gophertiles_155.jpg */,
+                               B2E6FEAA1AC2EFA200D557D8 /* gophertiles_156.jpg */,
+                               B2E6FEAB1AC2EFA200D557D8 /* gophertiles_157.jpg */,
+                               B2E6FEAC1AC2EFA200D557D8 /* gophertiles_158.jpg */,
+                               B2E6FEAD1AC2EFA200D557D8 /* gophertiles_159.jpg */,
+                               B2E6FEAE1AC2EFA200D557D8 /* gophertiles_160.jpg */,
+                               B2E6FEAF1AC2EFA200D557D8 /* gophertiles_161.jpg */,
+                               B2E6FEB01AC2EFA200D557D8 /* gophertiles_162.jpg */,
+                               B2E6FEB11AC2EFA200D557D8 /* gophertiles_163.jpg */,
+                               B2E6FEB21AC2EFA200D557D8 /* gophertiles_164.jpg */,
+                               B2E6FEB31AC2EFA200D557D8 /* gophertiles_165.jpg */,
+                               B2E6FEB41AC2EFA200D557D8 /* gophertiles_166.jpg */,
+                               B2E6FEB51AC2EFA300D557D8 /* gophertiles_167.jpg */,
+                               B2E6FEB61AC2EFA300D557D8 /* gophertiles_168.jpg */,
+                               B2E6FEB71AC2EFA300D557D8 /* gophertiles_169.jpg */,
+                               B2E6FEB81AC2EFA300D557D8 /* gophertiles_170.jpg */,
+                               B2E6FEB91AC2EFA300D557D8 /* gophertiles_171.jpg */,
+                               B2E6FEBA1AC2EFA300D557D8 /* gophertiles_172.jpg */,
+                               B2E6FEBB1AC2EFA300D557D8 /* gophertiles_173.jpg */,
+                               B2E6FEBC1AC2EFA300D557D8 /* gophertiles_174.jpg */,
+                               B2E6FEBD1AC2EFA300D557D8 /* gophertiles_175.jpg */,
+                               B2E6FEBE1AC2EFA300D557D8 /* gophertiles_176.jpg */,
+                               B2E6FEBF1AC2EFA300D557D8 /* gophertiles_177.jpg */,
+                               B2E6FEC01AC2EFA300D557D8 /* gophertiles_178.jpg */,
+                               B2E6FEC11AC2EFA300D557D8 /* gophertiles_179.jpg */,
+                               B2E6FEC21AC2EFA300D557D8 /* gophertiles_180.jpg */,
+                       );
+                       path = 004;
+                       sourceTree = "<group>";
+               };
+               B2E6FEC51AC2EFA300D557D8 /* 006 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FEC61AC2EFA300D557D8 /* 006.css */,
+                               B2E6FEC71AC2EFA300D557D8 /* 006.js */,
+                       );
+                       path = 006;
+                       sourceTree = "<group>";
+               };
+               B2E6FEC91AC2EFA300D557D8 /* 007 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FECA1AC2EFA300D557D8 /* 007.py */,
+                       );
+                       path = 007;
+                       sourceTree = "<group>";
+               };
+               B2E6FED01AC2EFA300D557D8 /* files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E6FED11AC2EFA300D557D8 /* empty.txt */,
+                       );
+                       path = files;
+                       sourceTree = "<group>";
+               };
+               B2E708C71AE64AC00009EDAF /* bin */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B2E708C81AE64AC00009EDAF /* php-wrapper */,
+                               B2B170B41B2F02C100EDC007 /* testrun */,
+                       );
+                       path = bin;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXLegacyTarget section */
+               B2546D8A1A7F76FA0033A875 /* mod_h2 make */ = {
+                       isa = PBXLegacyTarget;
+                       buildArgumentsString = "$(ACTION) APXS=../gen/install/bin/apxs";
+                       buildConfigurationList = B2546D8B1A7F76FA0033A875 /* Build configuration list for PBXLegacyTarget "mod_h2 make" */;
+                       buildPhases = (
+                       );
+                       buildToolPath = /usr/bin/make;
+                       buildWorkingDirectory = "/Users/sei/projects/mod-h2/mod_h2";
+                       dependencies = (
+                       );
+                       name = "mod_h2 make";
+                       passBuildSettingsInEnvironment = 1;
+                       productName = "mod_h2 make";
+               };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXProject section */
+               B27BBD201A65757700C58A41 /* Project object */ = {
+                       isa = PBXProject;
+                       attributes = {
+                               LastUpgradeCheck = 0600;
+                               TargetAttributes = {
+                                       B2546D8A1A7F76FA0033A875 = {
+                                               CreatedOnToolsVersion = 6.0;
+                                       };
+                               };
+                       };
+                       buildConfigurationList = B27BBD231A65757700C58A41 /* Build configuration list for PBXProject "mod-h2" */;
+                       compatibilityVersion = "Xcode 3.2";
+                       developmentRegion = English;
+                       hasScannedForEncodings = 0;
+                       knownRegions = (
+                               en,
+                       );
+                       mainGroup = B27BBD1F1A65757700C58A41;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               B2546D8A1A7F76FA0033A875 /* mod_h2 make */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin XCBuildConfiguration section */
+               B2546D8C1A7F76FA0033A875 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUGGING_SYMBOLS = YES;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               MACOSX_DEPLOYMENT_TARGET = 10.9;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               OTHER_CFLAGS = "";
+                               OTHER_LDFLAGS = "";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
+                       };
+                       name = Debug;
+               };
+               B2546D8D1A7F76FA0033A875 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               COPY_PHASE_STRIP = YES;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               ENABLE_NS_ASSERTIONS = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               MACOSX_DEPLOYMENT_TARGET = 10.9;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               OTHER_CFLAGS = "";
+                               OTHER_LDFLAGS = "";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
+                       };
+                       name = Release;
+               };
+               B27BBD241A65757700C58A41 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BuildMake = "";
+                               MACOSX_DEPLOYMENT_TARGET = 10.9;
+                       };
+                       name = Debug;
+               };
+               B27BBD251A65757700C58A41 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BuildMake = "";
+                               MACOSX_DEPLOYMENT_TARGET = 10.9;
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               B2546D8B1A7F76FA0033A875 /* Build configuration list for PBXLegacyTarget "mod_h2 make" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               B2546D8C1A7F76FA0033A875 /* Debug */,
+                               B2546D8D1A7F76FA0033A875 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               B27BBD231A65757700C58A41 /* Build configuration list for PBXProject "mod-h2" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               B27BBD241A65757700C58A41 /* Debug */,
+                               B27BBD251A65757700C58A41 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = B27BBD201A65757700C58A41 /* Project object */;
+}
diff --git a/modules/http2/mod-h2.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/modules/http2/mod-h2.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644 (file)
index 0000000..1e3d739
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:mod-h2.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/modules/http2/mod-h2.xcodeproj/project.xcworkspace/xcshareddata/mod-h2.xccheckout b/modules/http2/mod-h2.xcodeproj/project.xcworkspace/xcshareddata/mod-h2.xccheckout
new file mode 100644 (file)
index 0000000..d722efd
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>IDESourceControlProjectFavoriteDictionaryKey</key>
+       <false/>
+       <key>IDESourceControlProjectIdentifier</key>
+       <string>0BBB75AC-83C1-4A66-A178-3C70F4067182</string>
+       <key>IDESourceControlProjectName</key>
+       <string>mod-h2</string>
+       <key>IDESourceControlProjectOriginsDictionary</key>
+       <dict>
+               <key>7A6D7B037166029D8EB565F124690A553EB43BAF</key>
+               <string>https://www.greenbytes.de/projects/mod-h2/mod-h2.git</string>
+               <key>ED37D202B9691A75DA1E927834B45B38A4B3B6A5</key>
+               <string>github.com:tatsuhiro-t/nghttp2.git</string>
+       </dict>
+       <key>IDESourceControlProjectPath</key>
+       <string>mod-h2/mod-h2.xcodeproj</string>
+       <key>IDESourceControlProjectRelativeInstallPathDictionary</key>
+       <dict>
+               <key>7A6D7B037166029D8EB565F124690A553EB43BAF</key>
+               <string>../../..</string>
+               <key>ED37D202B9691A75DA1E927834B45B38A4B3B6A5</key>
+               <string>../../../nghttp2/gen/nghttp2-master</string>
+       </dict>
+       <key>IDESourceControlProjectURL</key>
+       <string>https://www.greenbytes.de/projects/mod-h2/mod-h2.git</string>
+       <key>IDESourceControlProjectVersion</key>
+       <integer>111</integer>
+       <key>IDESourceControlProjectWCCIdentifier</key>
+       <string>7A6D7B037166029D8EB565F124690A553EB43BAF</string>
+       <key>IDESourceControlProjectWCConfigurations</key>
+       <array>
+               <dict>
+                       <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+                       <string>public.vcs.git</string>
+                       <key>IDESourceControlWCCIdentifierKey</key>
+                       <string>7A6D7B037166029D8EB565F124690A553EB43BAF</string>
+                       <key>IDESourceControlWCCName</key>
+                       <string>mod-h2</string>
+               </dict>
+               <dict>
+                       <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+                       <string>public.vcs.git</string>
+                       <key>IDESourceControlWCCIdentifierKey</key>
+                       <string>ED37D202B9691A75DA1E927834B45B38A4B3B6A5</string>
+                       <key>IDESourceControlWCCName</key>
+                       <string>nghttp2-master</string>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/modules/http2/mod-h2.xcodeproj/xcuserdata/sei.xcuserdatad/xcschemes/mod_h2 make.xcscheme b/modules/http2/mod-h2.xcodeproj/xcuserdata/sei.xcuserdatad/xcschemes/mod_h2 make.xcscheme
new file mode 100644 (file)
index 0000000..4df5872
--- /dev/null
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0600"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "B2546D8A1A7F76FA0033A875"
+               BuildableName = "mod_h2 make"
+               BlueprintName = "mod_h2 make"
+               ReferencedContainer = "container:mod-h2.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "B2546D8A1A7F76FA0033A875"
+            BuildableName = "mod_h2 make"
+            BlueprintName = "mod_h2 make"
+            ReferencedContainer = "container:mod-h2.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "B2546D8A1A7F76FA0033A875"
+            BuildableName = "mod_h2 make"
+            BlueprintName = "mod_h2 make"
+            ReferencedContainer = "container:mod-h2.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/modules/http2/mod-h2.xcodeproj/xcuserdata/sei.xcuserdatad/xcschemes/xcschememanagement.plist b/modules/http2/mod-h2.xcodeproj/xcuserdata/sei.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644 (file)
index 0000000..9b913c0
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>SchemeUserState</key>
+       <dict>
+               <key>mod_h2 make.xcscheme</key>
+               <dict>
+                       <key>orderHint</key>
+                       <integer>0</integer>
+               </dict>
+       </dict>
+       <key>SuppressBuildableAutocreation</key>
+       <dict>
+               <key>B2546D8A1A7F76FA0033A875</key>
+               <dict>
+                       <key>primary</key>
+                       <true/>
+               </dict>
+       </dict>
+</dict>
+</plist>
diff --git a/modules/http2/mod_h2/.gitignore b/modules/http2/mod_h2/.gitignore
new file mode 100644 (file)
index 0000000..2cc059a
--- /dev/null
@@ -0,0 +1,7 @@
+*.o
+*.slo
+*.lo
+*.la
+.libs
+Makefile.in
+Makefile
diff --git a/modules/http2/mod_h2/Makefile.am b/modules/http2/mod_h2/Makefile.am
new file mode 100644 (file)
index 0000000..16a7ec5
--- /dev/null
@@ -0,0 +1,94 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+SUBDIRS =
+
+ACLOCAL_AMFLAGS = -I m4
+
+lib_LTLIBRARIES    = mod_h2.la
+
+mod_h2_la_CPPFLAGS = -std=c99 -D_GNU_SOURCE -Werror
+mod_h2_la_LDFLAGS  = -module
+
+OBJECTS = \
+    h2_alpn.c \
+    h2_alt_svc.c \
+    h2_config.c \
+    h2_conn.c \
+    h2_conn_io.c \
+    h2_ctx.c \
+    h2_from_h1.c \
+    h2_h2.c \
+    h2_io.c \
+    h2_io_set.c \
+    h2_mplx.c \
+    h2_request.c \
+    h2_response.c \
+    h2_session.c \
+    h2_stream.c \
+    h2_stream_set.c \
+    h2_task.c \
+    h2_task_input.c \
+    h2_task_output.c \
+    h2_task_queue.c \
+    h2_to_h1.c \
+    h2_upgrade.c \
+    h2_util.c \
+    h2_worker.c \
+    h2_workers.c \
+    mod_h2.c
+
+HFILES = \
+    h2_alpn.h \
+    h2_alt_svc.h \
+    h2_config.h \
+    h2_conn.h \
+    h2_conn_io.h \
+    h2_ctx.h \
+    h2_from_h1.h \
+    h2_h2.h \
+    h2_io.h \
+    h2_io_set.h \
+    h2_mplx.h \
+    h2_private.h \
+    h2_request.h \
+    h2_response.h \
+    h2_session.h \
+    h2_stream.h \
+    h2_stream_set.h \
+    h2_task.h \
+    h2_task_input.h \
+    h2_task_output.h \
+    h2_task_queue.h \
+    h2_to_h1.h \
+    h2_upgrade.h \
+    h2_util.h \
+    h2_version.h \
+    h2_worker.h \
+    h2_workers.h \
+    mod_h2.h
+
+
+mod_h2_la_SOURCES = $(HFILES) $(OBJECTS)
+
+all: mod_h2.la
+
+install-libLTLIBRARIES:
+       @: # override
+
+install-exec-local: mod_h2.la
+       $(MKDIR_P) $(DESTDIR)/@LIBEXEC_DIR@
+       $(APXS) -i -S LIBEXECDIR=$(DESTDIR)/@LIBEXEC_DIR@ -n h2 mod_h2.la
+
+
diff --git a/modules/http2/mod_h2/h2_alpn.c b/modules/http2/mod_h2/h2_alpn.c
new file mode 100644 (file)
index 0000000..1b3e286
--- /dev/null
@@ -0,0 +1,295 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_strings.h>
+#include <apr_optional.h>
+#include <apr_optional_hooks.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_conn.h"
+#include "h2_h2.h"
+#include "h2_alpn.h"
+
+/*******************************************************************************
+ * SSL var lookup
+ */
+APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
+                        (apr_pool_t *, server_rec *,
+                         conn_rec *, request_rec *,
+                         char *));
+static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *,
+                                   conn_rec *, request_rec *,
+                                   char *);
+
+/*******************************************************************************
+ * NPN callbacks and registry, deprecated
+ */
+typedef int (*ssl_npn_advertise_protos)(conn_rec *connection, 
+    apr_array_header_t *protos);
+
+typedef int (*ssl_npn_proto_negotiated)(conn_rec *connection, 
+    const char *proto_name, apr_size_t proto_name_len);
+
+APR_DECLARE_OPTIONAL_FN(int, modssl_register_npn, 
+                        (conn_rec *conn,
+                         ssl_npn_advertise_protos advertisefn,
+                         ssl_npn_proto_negotiated negotiatedfn));
+
+static int (*opt_ssl_register_npn)(conn_rec*,
+                                   ssl_npn_advertise_protos,
+                                   ssl_npn_proto_negotiated);
+
+/*******************************************************************************
+ * ALPN callbacks and registry
+ */
+typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
+    apr_array_header_t *client_protos, apr_array_header_t *protos);
+
+typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
+    const char *proto_name, apr_size_t proto_name_len);
+
+APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
+                        (conn_rec *conn,
+                         ssl_alpn_propose_protos proposefn,
+                         ssl_alpn_proto_negotiated negotiatedfn));
+
+static int (*opt_ssl_register_alpn)(conn_rec*,
+                                    ssl_alpn_propose_protos,
+                                    ssl_alpn_proto_negotiated);
+
+/*******************************************************************************
+ * Hooks for processing incoming connections:
+ * - pre_conn_after_tls registers for ALPN handling
+ */
+static int h2_alpn_pre_conn(conn_rec* c, void *arg);
+
+/*******************************************************************************
+ * Once per lifetime init, retrieve optional functions
+ */
+apr_status_t h2_alpn_init(apr_pool_t *pool, server_rec *s)
+{
+    (void)pool;
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_alpn init");
+    opt_ssl_register_npn = APR_RETRIEVE_OPTIONAL_FN(modssl_register_npn);
+    opt_ssl_register_alpn = APR_RETRIEVE_OPTIONAL_FN(modssl_register_alpn);
+    opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
+
+    if (!opt_ssl_register_alpn && !opt_ssl_register_npn) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+                     "mod_ssl does not offer ALPN or NPN registration");
+    }
+    return APR_SUCCESS;
+}
+
+/*******************************************************************************
+ * Register various hooks
+ */
+static const char *const mod_ssl[]        = { "mod_ssl.c", NULL};
+static const char *const mod_core[]       = { "core.c", NULL};
+
+static void check_sni_host(conn_rec *c) 
+{
+    /* If we have not done so already, ask the connection for the
+     * hostname send to us via SNI. This information is later used
+     * to retrieve the correct server settings for this connection.
+     */
+    h2_ctx *ctx = h2_ctx_get(c);
+    if (opt_ssl_var_lookup && !ctx->hostname) {
+        const char *p = opt_ssl_var_lookup(c->pool, c->base_server, c, 
+                                           NULL, (char*)"SSL_TLS_SNI");
+        if (p && *p) {
+            ctx->hostname = apr_pstrdup(c->pool, p);
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                          "h2_h2, connection, SNI %s",
+                          ctx->hostname? ctx->hostname : "NULL");
+        }
+    }
+}
+
+void h2_alpn_register_hooks(void)
+{
+    /* This hook runs on new connection after mod_ssl, but before the core
+     * httpd. Its purpose is to register, if TLS is used, the ALPN callbacks
+     * that enable us to chose "h2" as next procotol if the client supports it.
+     */
+    ap_hook_pre_connection(h2_alpn_pre_conn, 
+                           mod_ssl, mod_core, APR_HOOK_LAST);
+    
+}
+
+static int h2_util_array_index(apr_array_header_t *array, const char *s)
+{
+    for (int i = 0; i < array->nelts; i++) {
+        const char *p = APR_ARRAY_IDX(array, i, const char*);
+        if (!strcmp(p, s)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static int h2_npn_advertise(conn_rec *c, apr_array_header_t *protos)
+{
+    h2_config *cfg;
+    
+    check_sni_host(c);
+    cfg = h2_config_get(c);
+    if (!h2_config_geti(cfg, H2_CONF_ENABLED)) {
+        return DECLINED;
+    }
+    
+    for (apr_size_t i = 0; i < h2_alpn_protos_len; ++i) {
+        const char *proto = h2_alpn_protos[i];
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "NPN proposing %s from client selection", proto);
+        APR_ARRAY_PUSH(protos, const char*) = proto;
+    }
+    return OK;
+}
+
+static int h2_negotiated(conn_rec *c, const char *via, 
+                         const char *proto_name,
+                         apr_size_t proto_name_len)
+{
+    h2_ctx *ctx = h2_ctx_get(c);
+
+    if (h2_ctx_is_task(ctx) ) {
+        return DECLINED;
+    }
+    
+    if (h2_ctx_pnego_is_done(ctx)) {
+        /* called twice? refraing from overriding existing selection.
+         * NPN is fading...
+         */
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "protocol negotiated via %s called, but already set", 
+                      via); 
+        return DECLINED;
+    }
+    
+    if (APLOGctrace1(c)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "protocol negotiated via %s is %s", via, 
+                      apr_pstrndup(c->pool, proto_name, proto_name_len));
+    }
+    
+    for (apr_size_t i = 0; i < h2_alpn_protos_len; ++i) {
+        const char *proto = h2_alpn_protos[i];
+        if (proto_name_len == strlen(proto)
+            && strncmp(proto, proto_name, proto_name_len) == 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, 
+                          "protocol set via %s to %s", via, proto);
+            h2_ctx_pnego_set_done(ctx, proto);
+            break;
+        }
+    }    
+    return OK;
+}
+
+static int h2_npn_negotiated(conn_rec *c,
+                             const char *proto_name,
+                             apr_size_t proto_name_len)
+{
+    return h2_negotiated(c, "NPN", proto_name, proto_name_len);
+}
+
+static int h2_alpn_propose(conn_rec *c,
+                           apr_array_header_t *client_protos,
+                           apr_array_header_t *protos)
+{
+    h2_config *cfg;
+    
+    check_sni_host(c);
+    cfg = h2_config_get(c);
+    if (!h2_config_geti(cfg, H2_CONF_ENABLED)) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+                      "ALPN propose, h2 disabled for config %s", cfg->name);
+        return DECLINED;
+    }
+    
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+                  "ALPN propose for config %s", cfg->name);
+    /* */
+    for (apr_size_t i = 0; i < h2_alpn_protos_len; ++i) {
+        const char *proto = h2_alpn_protos[i];
+        if (h2_util_array_index(client_protos, proto) >= 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                          "ALPN proposing %s", proto);
+            APR_ARRAY_PUSH(protos, const char*) = proto;
+            return OK; /* propose only one, the first match from our list */
+        }
+    }
+    return OK;
+}
+
+static int h2_alpn_negotiated(conn_rec *c,
+                              const char *proto_name,
+                              apr_size_t proto_name_len)
+{
+    return h2_negotiated(c, "ALPN", proto_name, proto_name_len);
+}
+
+
+
+int h2_alpn_pre_conn(conn_rec* c, void *arg)
+{
+    (void)arg;
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                  "h2_h2, pre_connection, start");
+    
+    h2_ctx *ctx = h2_ctx_get(c);
+    if (h2_ctx_is_task(ctx)) {
+        /* our stream pseudo connection */
+        return DECLINED;
+    }
+    
+    if (h2_h2_is_tls(c)) {
+        /* Brand new TLS connection: Does mod_ssl offer ALPN/NPN support? 
+         * If so, register at all present, clients may use either/or.
+         */
+        if (opt_ssl_register_alpn == NULL && opt_ssl_register_npn == NULL) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+                          "h2_h2, pre_connection, no ALPN/NPN "
+                          "support in mod_ssl");
+            return DECLINED;
+        }
+        
+        if (opt_ssl_register_alpn) {
+            opt_ssl_register_alpn(c, h2_alpn_propose, h2_alpn_negotiated);
+        }
+        if (opt_ssl_register_npn) {
+            opt_ssl_register_npn(c, h2_npn_advertise, h2_npn_negotiated);
+        }
+        
+        h2_ctx_pnego_set_started(ctx);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                      "h2_alpn, pre_connection, ALPN callback registered");
+    }
+    
+    return DECLINED;
+}
+
diff --git a/modules/http2/mod_h2/h2_alpn.h b/modules/http2/mod_h2/h2_alpn.h
new file mode 100644 (file)
index 0000000..ab6c8d4
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_alpn__
+#define __mod_h2__h2_alpn__
+
+/*
+ * One time, post config intialization.
+ */
+apr_status_t h2_alpn_init(apr_pool_t *pool, server_rec *s);
+
+/* Register apache hooks for ALPN protocol
+ */
+void h2_alpn_register_hooks(void);
+
+
+#endif /* defined(__mod_h2__h2_h2__) */
diff --git a/modules/http2/mod_h2/h2_alt_svc.c b/modules/http2/mod_h2/h2_alt_svc.c
new file mode 100644 (file)
index 0000000..37f7692
--- /dev/null
@@ -0,0 +1,122 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <apr_strings.h>
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_alt_svc.h"
+#include "h2_ctx.h"
+#include "h2_config.h"
+#include "h2_h2.h"
+#include "h2_util.h"
+
+static int h2_alt_svc_request_handler(request_rec *r);
+
+void h2_alt_svc_register_hooks(void)
+{
+    ap_hook_handler(h2_alt_svc_request_handler, NULL, NULL, APR_HOOK_LAST);
+}
+
+/**
+ * Parse an Alt-Svc specifier as described in "HTTP Alternative Services"
+ * (https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04)
+ * with the following changes:
+ * - do not percent encode token values
+ * - do not use quotation marks
+ */
+h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool) {
+    const char *sep = strchr(s, '=');
+    if (sep) {
+        const char *alpn = apr_pstrndup(pool, s, sep - s);
+        const char *host = NULL;
+        int port = 0;
+        s = sep + 1;
+        sep = strchr(s, ':');  /* mandatory : */
+        if (sep) {
+            if (sep != s) {    /* optional host */
+                host = apr_pstrndup(pool, s, sep - s);
+            }
+            s = sep + 1;
+            if (*s) {          /* must be a port number */
+                port = (int)apr_atoi64(s);
+                if (port > 0 && port < (0x1 << 16)) {
+                    h2_alt_svc *as = apr_pcalloc(pool, sizeof(*as));
+                    as->alpn = alpn;
+                    as->host = host;
+                    as->port = port;
+                    return as;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+#define h2_alt_svc_IDX(list, i) ((h2_alt_svc**)(list)->elts)[i]
+
+static int h2_alt_svc_request_handler(request_rec *r)
+{
+    h2_ctx *ctx = h2_ctx_rget(r);
+    
+    if (h2_ctx_is_active(ctx) || h2_ctx_is_task(ctx)) {
+        return DECLINED;
+    }
+    
+    h2_config *cfg = h2_config_rget(r);
+    if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) {
+        const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used");
+        if (!alt_svc_used /*|| (alt_svc_used[0] == '0')*/) {
+            /* We have alt-svcs defined and client is not already using
+             * one, announce the services that were configured and match. 
+             * The security of this connection determines if we allow
+             * other host names or ports only.
+             */
+            const char *alt_svc = "";
+            const char *svc_ma = "";
+            int secure = h2_h2_is_tls(r->connection);
+            int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE);
+            if (ma >= 0) {
+                svc_ma = apr_psprintf(r->pool, "; ma=%d", ma);
+            }
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "h2_alt_svc: announce %s for %s:%d", 
+                          (secure? "secure" : "insecure"), 
+                          r->hostname, (int)r->server->port);
+            for (int i = 0; i < cfg->alt_svcs->nelts; ++i) {
+                h2_alt_svc *as = h2_alt_svc_IDX(cfg->alt_svcs, i);
+                const char *ahost = as->host;
+                if (ahost && !apr_strnatcasecmp(ahost, r->hostname)) {
+                    ahost = NULL;
+                }
+                if (secure || !ahost) {
+                    alt_svc = apr_psprintf(r->pool, "%s%s%s=\"%s:%d\"%s", 
+                                           alt_svc,
+                                           (*alt_svc? ", " : ""), as->alpn,
+                                           ahost? ahost : "", as->port,
+                                           svc_ma);
+                }
+            }
+            if (*alt_svc) {
+                apr_table_set(r->headers_out, "Alt-Svc", alt_svc);
+            }
+        }
+    }
+    
+    return DECLINED;
+}
+
diff --git a/modules/http2/mod_h2/h2_alt_svc.h b/modules/http2/mod_h2/h2_alt_svc.h
new file mode 100644 (file)
index 0000000..51f89d0
--- /dev/null
@@ -0,0 +1,39 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_alt_svc__
+#define __mod_h2__h2_alt_svc__
+
+typedef struct h2_alt_svc h2_alt_svc;
+
+struct h2_alt_svc {
+    const char *alpn;
+    const char *host;
+    int port;
+};
+
+void h2_alt_svc_register_hooks(void);
+
+/**
+ * Parse an Alt-Svc specifier as described in "HTTP Alternative Services"
+ * (https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04)
+ * with the following changes:
+ * - do not percent encode token values
+ * - do not use quotation marks
+ */
+h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool);
+
+
+#endif /* defined(__mod_h2__h2_alt_svc__) */
diff --git a/modules/http2/mod_h2/h2_config.c b/modules/http2/mod_h2/h2_config.c
new file mode 100644 (file)
index 0000000..01b6fb9
--- /dev/null
@@ -0,0 +1,384 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <http_vhost.h>
+
+#include <apr_strings.h>
+
+#include "h2_alt_svc.h"
+#include "h2_ctx.h"
+#include "h2_conn.h"
+#include "h2_config.h"
+#include "h2_private.h"
+
+#define DEF_VAL     (-1)
+
+#define H2_CONFIG_GET(a, b, n) \
+    (((a)->n == DEF_VAL)? (b) : (a))->n
+
+static h2_config defconf = {
+    "default",
+    0,                /* enabled */
+    100,              /* max_streams */
+    16 * 1024,        /* max_hl_size */
+    64 * 1024,        /* window_size */
+    -1,               /* min workers */
+    -1,               /* max workers */
+    10 * 60,          /* max workers idle secs */
+    64 * 1024,        /* stream max mem size */
+    NULL,             /* no alt-svcs */
+    -1,               /* alt-svc max age */
+    0,                /* serialize headers */
+    1,                /* hack mpm event */
+    1,                /* h2 direct mode */
+};
+
+static void *h2_config_create(apr_pool_t *pool,
+                              const char *prefix, const char *x)
+{
+    h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
+    
+    const char *s = x? x : "unknown";
+    char *name = (char *)apr_pcalloc(pool, strlen(prefix) + strlen(s) + 20);
+    strcpy(name, prefix);
+    strcat(name, "[");
+    strcat(name, s);
+    strcat(name, "]");
+    
+    conf->name           = name;
+    conf->h2_enabled     = DEF_VAL;
+    conf->h2_max_streams = DEF_VAL;
+    conf->h2_max_hl_size = DEF_VAL;
+    conf->h2_window_size = DEF_VAL;
+    conf->min_workers    = DEF_VAL;
+    conf->max_workers    = DEF_VAL;
+    conf->max_worker_idle_secs = DEF_VAL;
+    conf->stream_max_mem_size = DEF_VAL;
+    conf->alt_svc_max_age = DEF_VAL;
+    conf->serialize_headers = DEF_VAL;
+    conf->hack_mpm_event = DEF_VAL;
+    conf->h2_direct      = DEF_VAL;
+    return conf;
+}
+
+void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
+{
+    return h2_config_create(pool, "srv", s->defn_name);
+}
+
+void *h2_config_create_dir(apr_pool_t *pool, char *x)
+{
+    return h2_config_create(pool, "dir", x);
+}
+
+void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
+{
+    h2_config *base = (h2_config *)basev;
+    h2_config *add = (h2_config *)addv;
+    h2_config *n = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
+
+    char *name = (char *)apr_pcalloc(pool,
+        20 + strlen(add->name) + strlen(base->name));
+    strcpy(name, "merged[");
+    strcat(name, add->name);
+    strcat(name, ", ");
+    strcat(name, base->name);
+    strcat(name, "]");
+    n->name = name;
+
+    n->h2_enabled     = H2_CONFIG_GET(add, base, h2_enabled);
+    n->h2_max_streams = H2_CONFIG_GET(add, base, h2_max_streams);
+    n->h2_max_hl_size = H2_CONFIG_GET(add, base, h2_max_hl_size);
+    n->h2_window_size = H2_CONFIG_GET(add, base, h2_window_size);
+    n->min_workers    = H2_CONFIG_GET(add, base, min_workers);
+    n->max_workers    = H2_CONFIG_GET(add, base, max_workers);
+    n->max_worker_idle_secs = H2_CONFIG_GET(add, base, max_worker_idle_secs);
+    n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size);
+    n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs;
+    n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age);
+    n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers);
+    n->hack_mpm_event = H2_CONFIG_GET(add, base, hack_mpm_event);
+    n->h2_direct      = H2_CONFIG_GET(add, base, h2_direct);
+    
+    return n;
+}
+
+int h2_config_geti(h2_config *conf, h2_config_var_t var)
+{
+    switch(var) {
+        case H2_CONF_ENABLED:
+            return H2_CONFIG_GET(conf, &defconf, h2_enabled);
+        case H2_CONF_MAX_STREAMS:
+            return H2_CONFIG_GET(conf, &defconf, h2_max_streams);
+        case H2_CONF_MAX_HL_SIZE:
+            return H2_CONFIG_GET(conf, &defconf, h2_max_hl_size);
+        case H2_CONF_WIN_SIZE:
+            return H2_CONFIG_GET(conf, &defconf, h2_window_size);
+        case H2_CONF_MIN_WORKERS:
+            return H2_CONFIG_GET(conf, &defconf, min_workers);
+        case H2_CONF_MAX_WORKERS:
+            return H2_CONFIG_GET(conf, &defconf, max_workers);
+        case H2_CONF_MAX_WORKER_IDLE_SECS:
+            return H2_CONFIG_GET(conf, &defconf, max_worker_idle_secs);
+        case H2_CONF_STREAM_MAX_MEM:
+            return H2_CONFIG_GET(conf, &defconf, stream_max_mem_size);
+        case H2_CONF_ALT_SVC_MAX_AGE:
+            return H2_CONFIG_GET(conf, &defconf, alt_svc_max_age);
+        case H2_CONF_SER_HEADERS:
+            return H2_CONFIG_GET(conf, &defconf, serialize_headers);
+        case H2_CONF_HACK_MPM_EVENT:
+            return H2_CONFIG_GET(conf, &defconf, hack_mpm_event);
+        case H2_CONF_DIRECT:
+            return H2_CONFIG_GET(conf, &defconf, h2_direct);
+        default:
+            return DEF_VAL;
+    }
+}
+
+h2_config *h2_config_sget(server_rec *s)
+{
+    h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, 
+                                                       &h2_module);
+    AP_DEBUG_ASSERT(cfg);
+    return cfg;
+}
+
+
+static const char *h2_conf_set_engine(cmd_parms *parms,
+                                      void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    if (!strcasecmp(value, "On")) {
+        cfg->h2_enabled = 1;
+        return NULL;
+    }
+    else if (!strcasecmp(value, "Off")) {
+        cfg->h2_enabled = 0;
+        return NULL;
+    }
+    
+    (void)arg;
+    return "value must be On or Off";
+}
+
+static const char *h2_conf_set_max_streams(cmd_parms *parms,
+                                           void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->h2_max_streams = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_window_size(cmd_parms *parms,
+                                           void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->h2_window_size = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_max_hl_size(cmd_parms *parms,
+                                           void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->h2_max_hl_size = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_min_workers(cmd_parms *parms,
+                                           void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->min_workers = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_max_workers(cmd_parms *parms,
+                                           void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->max_workers = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms,
+                                                    void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->max_worker_idle_secs = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms,
+                                                   void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    
+    
+    cfg->stream_max_mem_size = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_add_alt_svc(cmd_parms *parms,
+                                  void *arg, const char *value)
+{
+    if (value && strlen(value)) {
+        h2_config *cfg = h2_config_sget(parms->server);
+        h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool);
+        if (!as) {
+            return "unable to parse alt-svc specifier";
+        }
+        if (!cfg->alt_svcs) {
+            cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*));
+        }
+        APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
+    }
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms,
+                                               void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->alt_svc_max_age = (int)apr_atoi64(value);
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
+                                                 void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->serialize_headers = !apr_strnatcasecmp(value, "On");
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_hack_mpm_event(cmd_parms *parms,
+                                              void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->hack_mpm_event = !apr_strnatcasecmp(value, "On");
+    (void)arg;
+    return NULL;
+}
+
+static const char *h2_conf_set_direct(cmd_parms *parms,
+                                      void *arg, const char *value)
+{
+    h2_config *cfg = h2_config_sget(parms->server);
+    cfg->h2_direct = !apr_strnatcasecmp(value, "On");
+    (void)arg;
+    return NULL;
+}
+
+#pragma GCC diagnostic ignored "-Wmissing-braces"
+const command_rec h2_cmds[] = {
+    AP_INIT_TAKE1("H2Engine", h2_conf_set_engine, NULL,
+                  RSRC_CONF, "on to enable HTTP/2 protocol handling"),
+    AP_INIT_TAKE1("H2MaxSessionStreams", h2_conf_set_max_streams, NULL,
+                  RSRC_CONF, "maximum number of open streams per session"),
+    AP_INIT_TAKE1("H2InitialWindowSize", h2_conf_set_window_size, NULL,
+                  RSRC_CONF, "initial window size on client DATA"),
+    AP_INIT_TAKE1("H2MaxHeaderListSize", h2_conf_set_max_hl_size, NULL, 
+                  RSRC_CONF, "maximum acceptable size of request headers"),
+    AP_INIT_TAKE1("H2MinWorkers", h2_conf_set_min_workers, NULL,
+                  RSRC_CONF, "minimum number of worker threads per child"),
+    AP_INIT_TAKE1("H2MaxWorkers", h2_conf_set_max_workers, NULL,
+                  RSRC_CONF, "maximum number of worker threads per child"),
+    AP_INIT_TAKE1("H2MaxWorkerIdleSeconds", h2_conf_set_max_worker_idle_secs, NULL,
+                  RSRC_CONF, "maximum number of idle seconds before a worker shuts down"),
+    AP_INIT_TAKE1("H2StreamMaxMemSize", h2_conf_set_stream_max_mem_size, NULL,
+                  RSRC_CONF, "maximum number of bytes buffered in memory for a stream"),
+    AP_INIT_TAKE1("H2AltSvc", h2_add_alt_svc, NULL,
+                  RSRC_CONF, "adds an Alt-Svc for this server"),
+    AP_INIT_TAKE1("H2AltSvcMaxAge", h2_conf_set_alt_svc_max_age, NULL,
+                  RSRC_CONF, "set the maximum age (in seconds) that client can rely on alt-svc information"),
+    AP_INIT_TAKE1("H2SerializeHeaders", h2_conf_set_serialize_headers, NULL,
+                  RSRC_CONF, "on to enable header serialization for compatibility"),
+    AP_INIT_TAKE1("H2HackMpmEvent", h2_conf_set_hack_mpm_event, NULL,
+                  RSRC_CONF, "on to enable a hack that makes mpm_event working with mod_h2"),
+    AP_INIT_TAKE1("H2Direct", h2_conf_set_direct, NULL,
+                  RSRC_CONF, "on to enable direct HTTP/2 mode on non-TLS"),
+    { NULL, NULL, NULL, 0, 0, NULL }
+};
+
+
+h2_config *h2_config_rget(request_rec *r)
+{
+    h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, 
+                                                       &h2_module);
+    return cfg? cfg : h2_config_sget(r->server); 
+}
+
+h2_config *h2_config_get(conn_rec *c)
+{
+    h2_ctx *ctx = h2_ctx_get(c);
+    if (ctx->config) {
+        return ctx->config;
+    }
+    if (!ctx->server && ctx->hostname) {
+        /* We have a host agreed upon via TLS SNI, but no request yet.
+         * The sni host was accepted and therefore does match a server record
+         * (vhost) for it. But we need to know which one.
+         * Normally, it is enough to be set on the initial request on a
+         * connection, but we need it earlier. Simulate a request and call
+         * the vhost matching stuff.
+         */
+        apr_uri_t uri;
+        memset(&uri, 0, sizeof(uri));
+        uri.scheme = (char*)"https";
+        uri.hostinfo = (char*)ctx->hostname;
+        uri.hostname = (char*)ctx->hostname;
+        uri.port_str = (char*)"";
+        uri.port = c->local_addr->port;
+        uri.path = (char*)"/";
+        
+        request_rec r;
+        memset(&r, 0, sizeof(r));
+        r.uri = (char*)"/";
+        r.connection = c;
+        r.pool = c->pool;
+        r.hostname = ctx->hostname;
+        r.headers_in = apr_table_make(c->pool, 1);
+        r.parsed_uri = uri;
+        r.status = HTTP_OK;
+        r.server = r.connection->base_server;
+        ap_update_vhost_from_headers(&r);
+        ctx->server = r.server;
+    }
+    
+    if (ctx->server) {
+        ctx->config = h2_config_sget(ctx->server);
+        return ctx->config;
+    }
+    
+    return h2_config_sget(c->base_server);
+}
+
diff --git a/modules/http2/mod_h2/h2_config.h b/modules/http2/mod_h2/h2_config.h
new file mode 100644 (file)
index 0000000..a379475
--- /dev/null
@@ -0,0 +1,78 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_config_h__
+#define __mod_h2__h2_config_h__
+
+#undef PACKAGE_VERSION
+#undef PACKAGE_TARNAME
+#undef PACKAGE_STRING
+#undef PACKAGE_NAME
+#undef PACKAGE_BUGREPORT
+#include "config.h"
+
+typedef enum {
+    H2_CONF_ENABLED,
+    H2_CONF_MAX_STREAMS,
+    H2_CONF_MAX_HL_SIZE,
+    H2_CONF_WIN_SIZE,
+    H2_CONF_MIN_WORKERS,
+    H2_CONF_MAX_WORKERS,
+    H2_CONF_MAX_WORKER_IDLE_SECS,
+    H2_CONF_STREAM_MAX_MEM,
+    H2_CONF_ALT_SVCS,
+    H2_CONF_ALT_SVC_MAX_AGE,
+    H2_CONF_SER_HEADERS,
+    H2_CONF_HACK_MPM_EVENT,
+    H2_CONF_DIRECT,
+} h2_config_var_t;
+
+/* Apache httpd module configuration for h2. */
+typedef struct h2_config {
+    const char *name;
+    int h2_enabled;               /* if mod_h2 is active at all here */
+    int h2_max_streams;           /* max concurrent # streams (http2) */
+    int h2_max_hl_size;           /* max header list size (http2) */
+    int h2_window_size;           /* stream window size (http2) */
+    int min_workers;              /* min # of worker threads/child */
+    int max_workers;              /* max # of worker threads/child */
+    int max_worker_idle_secs;     /* max # of idle seconds for worker */
+    int stream_max_mem_size;      /* max # bytes held in memory/stream */
+    apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */
+    int alt_svc_max_age;          /* seconds clients can rely on alt-svc info*/
+    int serialize_headers;        /* Use serialized HTTP/1.1 headers for 
+                                     processing, better compatibility */
+    int hack_mpm_event;           /* If mpm_event is detected, perform a hack
+                                     on stream connections to make it work */
+    int h2_direct;                /* if mod_h2 is active on non-TLS directly */
+} h2_config;
+
+
+void *h2_config_create_dir(apr_pool_t *pool, char *x);
+void *h2_config_create_svr(apr_pool_t *pool, server_rec *s);
+void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv);
+
+apr_status_t h2_config_apply_header(h2_config *config, request_rec *r);
+
+extern const command_rec h2_cmds[];
+
+h2_config *h2_config_get(conn_rec *c);
+h2_config *h2_config_sget(server_rec *s);
+h2_config *h2_config_rget(request_rec *r);
+
+int h2_config_geti(h2_config *conf, h2_config_var_t var);
+
+#endif /* __mod_h2__h2_config_h__ */
+
diff --git a/modules/http2/mod_h2/h2_conn.c b/modules/http2/mod_h2/h2_conn.c
new file mode 100644 (file)
index 0000000..3639d7e
--- /dev/null
@@ -0,0 +1,543 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <ap_mpm.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_request.h>
+
+#include "h2_private.h"
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_mplx.h"
+#include "h2_session.h"
+#include "h2_stream.h"
+#include "h2_stream_set.h"
+#include "h2_task.h"
+#include "h2_worker.h"
+#include "h2_workers.h"
+#include "h2_conn.h"
+
+static struct h2_workers *workers;
+
+static apr_status_t h2_session_process(h2_session *session);
+
+static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
+static module *mpm_module;
+static module *ssl_module;
+static int checked;
+
+static void check_modules() 
+{
+    if (!checked) {
+        for (int i = 0; ap_loaded_modules[i]; ++i) {
+            module *m = ap_loaded_modules[i];
+            if (!strcmp("event.c", m->name)) {
+                mpm_type = H2_MPM_EVENT;
+                mpm_module = m;
+            }
+            else if (!strcmp("worker.c", m->name)) {
+                mpm_type = H2_MPM_WORKER;
+                mpm_module = m;
+            }
+            else if (!strcmp("prefork.c", m->name)) {
+                mpm_type = H2_MPM_PREFORK;
+                mpm_module = m;
+            }
+            else if (!strcmp("mod_ssl.c", m->name)) {
+                ssl_module = m;
+            }
+        }
+        checked = 1;
+    }
+}
+
+apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
+{
+    h2_config *config = h2_config_sget(s);
+    apr_status_t status = APR_SUCCESS;
+    int minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
+    int maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
+    
+    int max_threads_per_child = 0;
+    ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
+    int threads_limit = 0;
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &threads_limit);
+    
+    if (minw <= 0) {
+        minw = max_threads_per_child;
+    }
+    if (maxw <= 0) {
+        maxw = threads_limit;
+        if (maxw < minw) {
+            maxw = minw;
+        }
+    }
+    
+    for (int i = 0; ap_loaded_modules[i]; ++i) {
+        module *m = ap_loaded_modules[i];
+        if (!strcmp("event.c", m->name)) {
+            mpm_type = H2_MPM_EVENT;
+            mpm_module = m;
+        }
+        else if (!strcmp("worker.c", m->name)) {
+            mpm_type = H2_MPM_WORKER;
+            mpm_module = m;
+        }
+        else if (!strcmp("prefork.c", m->name)) {
+            mpm_type = H2_MPM_PREFORK;
+            mpm_module = m;
+            /* prefork reports 1 thread per child, also as max */
+            if (maxw == 1) {
+                maxw = 8; /* number of cores maybe? */
+            }
+        }
+        else if (!strcmp("mod_ssl.c", m->name)) {
+            ssl_module = m;
+        }
+    }
+    
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                 "h2_workers: min=%d max=%d, mthrpchild=%d, thr_limit=%d", 
+                 minw, maxw, max_threads_per_child, threads_limit);
+    
+    workers = h2_workers_create(s, pool, minw, maxw);
+    int idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
+    h2_workers_set_max_idle_secs(workers, idle_secs);
+    
+    return status;
+}
+
+h2_mpm_type_t h2_conn_mpm_type(void) {
+    check_modules();
+    return mpm_type;
+}
+
+module *h2_conn_mpm_module(void) {
+    check_modules();
+    return mpm_module;
+}
+
+apr_status_t h2_conn_rprocess(request_rec *r)
+{
+    h2_config *config = h2_config_rget(r);
+    
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "h2_conn_process start");
+    if (!workers) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "workers not initialized");
+        return APR_EGENERAL;
+    }
+    
+    h2_session *session = h2_session_rcreate(r, config, workers);
+    if (!session) {
+        return APR_EGENERAL;
+    }
+    
+    return h2_session_process(session);
+}
+
+apr_status_t h2_conn_main(conn_rec *c)
+{
+    h2_config *config = h2_config_get(c);
+    
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_main start");
+    if (!workers) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "workers not initialized");
+        return APR_EGENERAL;
+    }
+    
+    h2_session *session = h2_session_create(c, config, workers);
+    if (!session) {
+        return APR_EGENERAL;
+    }
+    
+    return h2_session_process(session);
+}
+
+apr_status_t h2_session_process(h2_session *session)
+{
+    apr_status_t status = APR_SUCCESS;
+    int rv = 0;
+    
+    /* Start talking to the client. Apart from protocol meta data,
+     * we mainly will see new http/2 streams opened by the client, which
+     * basically are http requests we need to dispatch.
+     *
+     * There will be bursts of new streams, to be served concurrently,
+     * followed by long pauses of no activity.
+     *
+     * Since the purpose of http/2 is to allow siumultaneous streams, we
+     * need to dispatch the handling of each stream into a separate worker
+     * thread, keeping this thread open for sending responses back as
+     * soon as they arrive.
+     * At the same time, we need to continue reading new frames from
+     * our client, which may be meta (WINDOWS_UPDATEs, PING, SETTINGS) or
+     * new streams.
+     *
+     * As long as we have streams open in this session, we cannot really rest
+     * since there are two conditions to wait on: 1. new data from the client,
+     * 2. new data from the open streams to send back.
+     *
+     * Only when we have no more streams open, can we do a blocking read
+     * on our connection.
+     *
+     * TODO: implement graceful GO_AWAY after configurable idle time
+     */
+    
+    ap_update_child_status_from_conn(session->c->sbh, SERVER_BUSY_READ, 
+                                     session->c);
+
+    if (APLOGctrace2(session->c)) {
+        ap_filter_t *filter = session->c->input_filters;
+        while (filter) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                          "h2_conn(%ld), has connection filter %s",
+                          session->id, filter->frec->name);
+            filter = filter->next;
+        }
+    }
+    
+    status = h2_session_start(session, &rv);
+    
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
+                  "h2_session(%ld): starting on %s:%d", session->id,
+                  session->c->base_server->defn_name,
+                  session->c->local_addr->port);
+    if (status != APR_SUCCESS) {
+        h2_session_abort(session, status, rv);
+        h2_session_destroy(session);
+        return status;
+    }
+    
+    apr_interval_time_t wait_micros = 0;
+    static const int MAX_WAIT_MICROS = 200 * 1000;
+    
+    while (!h2_session_is_done(session)) {
+        int have_written = 0;
+        int have_read = 0;
+        
+        status = h2_session_write(session, wait_micros);
+        if (status == APR_SUCCESS) {
+            have_written = 1;
+            wait_micros = 0;
+        }
+        else if (status == APR_EAGAIN) {
+            /* nop */
+        }
+        else if (status == APR_TIMEUP) {
+            wait_micros *= 2;
+            if (wait_micros > MAX_WAIT_MICROS) {
+                wait_micros = MAX_WAIT_MICROS;
+            }
+        }
+        else {
+            ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+                          "h2_session(%ld): writing, terminating",
+                          session->id);
+            h2_session_abort(session, status, 0);
+            break;
+        }
+        
+        /* We would like to do blocking reads as often as possible as they
+         * are more efficient in regard to server resources.
+         * We can do them under the following circumstances:
+         * - we have no open streams and therefore have nothing to write
+         * - we have just started the session and are waiting for the first
+         *   two frames to come in. There will always be at least 2 frames as
+         *   * h2 will send SETTINGS and SETTINGS-ACK
+         *   * h2c will count the header settings as one frame and we
+         *     submit our settings and need the ACK.
+         */
+        int got_streams = !h2_stream_set_is_empty(session->streams);
+        status = h2_session_read(session, 
+                                 (!got_streams 
+                                  || session->frames_received <= 1)?
+                                 APR_BLOCK_READ : APR_NONBLOCK_READ);
+        switch (status) {
+            case APR_SUCCESS:
+                /* successful read, reset our idle timers */
+                have_read = 1;
+                wait_micros = 0;
+                break;
+            case APR_EAGAIN:
+                break;
+            case APR_EBADF:
+            case APR_EOF:
+            case APR_ECONNABORTED:
+            case ECONNRESET:
+                ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+                              "h2_session(%ld): reading",
+                              session->id);
+                h2_session_abort(session, status, 0);
+                break;
+            default:
+                ap_log_cerror( APLOG_MARK, APLOG_WARNING, status, session->c,
+                              "h2_session(%ld): error reading, terminating",
+                              session->id);
+                h2_session_abort(session, status, 0);
+                break;
+        }
+        
+        if (!have_read && !have_written) {
+            /* Nothing to read or write, we may have sessions, but
+             * the have no data yet ready to be delivered. Slowly
+             * back off to give others a chance to do their work.
+             */
+            if (wait_micros == 0) {
+                wait_micros = 10;
+            }
+        }
+    }
+    
+    ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+                  "h2_session(%ld): done", session->id);
+    
+    ap_update_child_status_from_conn(session->c->sbh, SERVER_CLOSING, 
+                                     session->c);
+
+    h2_session_close(session);
+    h2_session_destroy(session);
+    
+    return DONE;
+}
+
+
+static void fix_event_conn(conn_rec *c, conn_rec *master);
+
+conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool)
+{
+    apr_socket_t *socket;
+    
+    AP_DEBUG_ASSERT(master);
+    
+    /* CAVEAT: it seems necessary to setup the conn_rec in the master
+     * connection thread. Other attempts crashed. 
+     * HOWEVER: we setup the connection using the pools and other items
+     * from the master connection, since we do not want to allocate 
+     * lots of resources here. 
+     * Lets allocated pools and everything else when we actually start
+     * working on this new connection.
+     */
+    /* Not sure about the scoreboard handle. Reusing the one from the main
+     * connection could make sense, is not really correct, but we cannot
+     * easily create new handles for our worker threads either.
+     * TODO
+     */
+    socket = ap_get_module_config(master->conn_config, &core_module);
+    conn_rec *c = ap_run_create_connection(pool, master->base_server,
+                                           socket,
+                                           master->id^((long)pool), 
+                                           master->sbh,
+                                           master->bucket_alloc);
+    if (c == NULL) {
+        ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
+                      "h2_task: creating conn");
+        return NULL;
+    }
+    return c;
+}
+
+apr_status_t h2_conn_prep(h2_task_env *env, conn_rec *master, h2_worker *worker)
+{
+    h2_config *cfg = h2_config_get(master);
+    
+    ap_log_perror(APLOG_MARK, APLOG_TRACE3, 0, env->pool,
+                  "h2_conn(%ld): created from master", master->id);
+    
+    /* Ok, we are just about to start processing the connection and
+     * the worker is calling us to setup all necessary resources.
+     * We can borrow some from the worker itself and some we do as
+     * sub-resources from it, so that we get a nice reuse of
+     * pools.
+     */
+    env->c.pool = env->pool;
+    env->c.bucket_alloc = h2_worker_get_bucket_alloc(worker);
+    env->c.current_thread = h2_worker_get_thread(worker);
+    
+    env->c.conn_config = ap_create_conn_config(env->pool);
+    env->c.notes = apr_table_make(env->pool, 5);
+
+    ap_set_module_config(env->c.conn_config, &core_module, 
+                         h2_worker_get_socket(worker));
+    
+    if (ssl_module) {
+        /* See #19, there is a range of SSL variables to be gotten from
+         * the main connection that should be available in request handlers
+         */
+        void *sslcfg = ap_get_module_config(master->conn_config, ssl_module);
+        if (sslcfg) {
+            ap_set_module_config(env->c.conn_config, ssl_module, sslcfg);
+        }
+    }
+    
+    /* This works for mpm_worker so far. Other mpm modules have 
+     * different needs, unfortunately. The most interesting one 
+     * being mpm_event...
+     */
+    switch (h2_conn_mpm_type()) {
+        case H2_MPM_WORKER:
+            /* all fine */
+            break;
+        case H2_MPM_EVENT: 
+            if (h2_config_geti(cfg, H2_CONF_HACK_MPM_EVENT)) {
+                fix_event_conn(&env->c, master);
+            }
+            break;
+        default:
+            /* fingers crossed */
+            break;
+    }
+    
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_setup(struct h2_task_env *env, struct h2_worker *worker)
+{
+    return h2_conn_prep(env, env->mplx->c, worker);
+}
+
+apr_status_t h2_conn_init(struct h2_task_env *env, struct h2_worker *worker)
+{
+    conn_rec *master = env->mplx->c;
+    h2_config *cfg = h2_config_get(master);
+    
+    apr_socket_t *socket = ap_get_module_config(master->conn_config, 
+                                                &core_module);
+    conn_rec *c = ap_run_create_connection(env->pool, master->base_server,
+                                           socket,
+                                           master->id^((long)env->pool), 
+                                           master->sbh,
+                                           master->bucket_alloc);
+    if (c == NULL) {
+        ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, env->pool,
+                      "h2_task: creating conn");
+        return APR_ENOMEM;
+    }
+    
+    env->c = *c;
+    env->c.bucket_alloc = h2_worker_get_bucket_alloc(worker);
+    env->c.current_thread = h2_worker_get_thread(worker);
+    
+    ap_set_module_config(env->c.conn_config, &core_module, socket);
+    if (ssl_module) {
+        /* See #19, there is a range of SSL variables to be gotten from
+         * the main connection that should be available in request handlers
+         */
+        void *sslcfg = ap_get_module_config(master->conn_config, ssl_module);
+        if (sslcfg) {
+            ap_set_module_config(env->c.conn_config, ssl_module, sslcfg);
+        }
+    }
+    
+    /* This works for mpm_worker so far. Other mpm modules have 
+     * different needs, unfortunately. The most interesting one 
+     * being mpm_event...
+     */
+    switch (h2_conn_mpm_type()) {
+        case H2_MPM_WORKER:
+            /* all fine */
+            break;
+        case H2_MPM_EVENT: 
+            if (h2_config_geti(cfg, H2_CONF_HACK_MPM_EVENT)) {
+                fix_event_conn(&env->c, master);
+            }
+            break;
+        default:
+            /* fingers crossed */
+            break;
+    }
+    
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_post(conn_rec *c, h2_worker *worker)
+{
+    (void)worker;
+    
+    /* be sure no one messes with this any more */
+    memset(c, 0, sizeof(*c)); 
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_process(conn_rec *c, apr_socket_t *socket)
+{
+    AP_DEBUG_ASSERT(c);
+    
+    c->clogging_input_filters = 1;
+    ap_process_connection(c, socket);
+    
+    return APR_SUCCESS;
+}
+
+/* This is an internal mpm event.c struct which is disguised
+ * as a conn_state_t so that mpm_event can have special connection
+ * state information without changing the struct seen on the outside.
+ *
+ * For our task connections we need to create a new beast of this type
+ * and fill it with enough meaningful things that mpm_event reads and
+ * starts processing out task request.
+ */
+typedef struct event_conn_state_t event_conn_state_t;
+struct event_conn_state_t {
+    /** APR_RING of expiration timeouts */
+    APR_RING_ENTRY(event_conn_state_t) timeout_list;
+    /** the expiration time of the next keepalive timeout */
+    apr_time_t expiration_time;
+    /** connection record this struct refers to */
+    conn_rec *c;
+    /** request record (if any) this struct refers to */
+    request_rec *r;
+    /** is the current conn_rec suspended?  (disassociated with
+     * a particular MPM thread; for suspend_/resume_connection
+     * hooks)
+     */
+    int suspended;
+    /** memory pool to allocate from */
+    apr_pool_t *p;
+    /** bucket allocator */
+    apr_bucket_alloc_t *bucket_alloc;
+    /** poll file descriptor information */
+    apr_pollfd_t pfd;
+    /** public parts of the connection state */
+    conn_state_t pub;
+};
+APR_RING_HEAD(timeout_head_t, event_conn_state_t);
+
+static void fix_event_conn(conn_rec *c, conn_rec *master) 
+{
+    event_conn_state_t *master_cs = ap_get_module_config(master->conn_config, 
+                                                         h2_conn_mpm_module());
+    event_conn_state_t *cs = apr_pcalloc(c->pool, sizeof(event_conn_state_t));
+    cs->bucket_alloc = apr_bucket_alloc_create(c->pool);
+    
+    ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cs);
+    
+    cs->c = c;
+    cs->r = NULL;
+    cs->p = master_cs->p;
+    cs->pfd = master_cs->pfd;
+    cs->pub = master_cs->pub;
+    cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
+    
+    c->cs = &(cs->pub);
+}
+
diff --git a/modules/http2/mod_h2/h2_conn.h b/modules/http2/mod_h2/h2_conn.h
new file mode 100644 (file)
index 0000000..795e1d6
--- /dev/null
@@ -0,0 +1,70 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_conn__
+#define __mod_h2__h2_conn__
+
+struct h2_task;
+struct h2_task_env;
+struct h2_worker;
+
+/* Process the connection that is now starting the HTTP/2
+ * conversation. Return when the HTTP/2 session is done
+ * and the connection will close.
+ */
+apr_status_t h2_conn_main(conn_rec *c);
+
+/* Process the request that has been upgraded to a HTTP/2
+ * conversation. Return when the HTTP/2 session is done
+ * and the connection will close.
+ */
+apr_status_t h2_conn_rprocess(request_rec *r);
+
+/* Initialize this child process for h2 connection work,
+ * to be called once during child init before multi processing
+ * starts.
+ */
+apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s);
+
+
+typedef enum {
+    H2_MPM_UNKNOWN,
+    H2_MPM_WORKER,
+    H2_MPM_EVENT,
+    H2_MPM_PREFORK,
+} h2_mpm_type_t;
+
+h2_mpm_type_t h2_conn_mpm_type();
+module *h2_conn_mpm_module();
+
+/* Returns the type of MPM module detected */
+h2_mpm_type_t h2_conn_mpm_type(void);
+
+/* Gives the detected module itself or NULL if unknown */
+module *h2_conn_mpm_module(void);
+
+
+conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *stream_pool);
+
+apr_status_t h2_conn_init(struct h2_task_env *env, struct h2_worker *worker);
+
+apr_status_t h2_conn_setup(struct h2_task_env *env, struct h2_worker *worker);
+apr_status_t h2_conn_prep(struct h2_task_env *env, conn_rec *master, 
+                          struct h2_worker *worker);
+apr_status_t h2_conn_post(conn_rec *c, struct h2_worker *worker);
+
+apr_status_t h2_conn_process(conn_rec *c, apr_socket_t *socket);
+
+#endif /* defined(__mod_h2__h2_conn__) */
diff --git a/modules/http2/mod_h2/h2_conn_io.c b/modules/http2/mod_h2/h2_conn_io.c
new file mode 100644 (file)
index 0000000..f29bbe3
--- /dev/null
@@ -0,0 +1,285 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <ap_mpm.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+
+#include "h2_private.h"
+#include "h2_conn_io.h"
+#include "h2_h2.h"
+#include "h2_util.h"
+
+/* If we write directly to our brigade or use a char buffer to collect
+ * out data.
+ */
+
+#define H2_CONN_IO_BUF_SIZE        (64 * 1024)
+#define H2_CONN_IO_SSL_WRITE_SIZE  (16 * 1024)
+
+
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c)
+{
+    io->connection = c;
+    io->input = apr_brigade_create(c->pool, c->bucket_alloc);
+    io->output = apr_brigade_create(c->pool, c->bucket_alloc);
+    io->buffer_output = h2_h2_is_tls(c);
+    io->buflen = 0;
+    
+    if (io->buffer_output) {
+        io->bufsize = H2_CONN_IO_BUF_SIZE;
+        io->buffer = apr_pcalloc(c->pool, io->bufsize);
+    }
+    else {
+        io->bufsize = 0;
+    }
+    
+    return APR_SUCCESS;
+}
+
+void h2_conn_io_destroy(h2_conn_io *io)
+{
+    io->input = NULL;
+    io->output = NULL;
+}
+
+static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io,
+                                           apr_read_type_e block,
+                                           h2_conn_io_on_read_cb on_read_cb,
+                                           void *puser, int *pdone)
+{
+    apr_status_t status = APR_SUCCESS;
+    apr_size_t readlen = 0;
+    *pdone = 0;
+    
+    while (status == APR_SUCCESS && !*pdone
+           && !APR_BRIGADE_EMPTY(io->input)) {
+        
+        apr_bucket* bucket = APR_BRIGADE_FIRST(io->input);
+        if (APR_BUCKET_IS_METADATA(bucket)) {
+            /* we do nothing regarding any meta here */
+        }
+        else {
+            const char *bucket_data = NULL;
+            apr_size_t bucket_length = 0;
+            status = apr_bucket_read(bucket, &bucket_data,
+                                     &bucket_length, block);
+            
+            if (status == APR_SUCCESS && bucket_length > 0) {
+                if (APLOGctrace2(io->connection)) {
+                    char buffer[32];
+                    h2_util_hex_dump(buffer, sizeof(buffer)/sizeof(buffer[0]),
+                                     bucket_data, bucket_length);
+                    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection,
+                                  "h2_conn_io(%ld): read %d bytes: %s",
+                                  io->connection->id, (int)bucket_length, buffer);
+                }
+                
+                if (bucket_length > 0) {
+                    apr_size_t consumed = 0;
+                    status = on_read_cb(bucket_data, bucket_length,
+                                        &consumed, pdone, puser);
+                    if (status == APR_SUCCESS && bucket_length > consumed) {
+                        /* We have data left in the bucket. Split it. */
+                        status = apr_bucket_split(bucket, consumed);
+                    }
+                    readlen += consumed;
+                }
+            }
+        }
+        apr_bucket_delete(bucket);
+    }
+    if (readlen == 0 && status == APR_SUCCESS && block == APR_NONBLOCK_READ) {
+        return APR_EAGAIN;
+    }
+    return status;
+}
+
+apr_status_t h2_conn_io_read(h2_conn_io *io,
+                             apr_read_type_e block,
+                             h2_conn_io_on_read_cb on_read_cb,
+                             void *puser)
+{
+    apr_status_t status;
+    int done = 0;
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection,
+                  "h2_conn_io: try read, block=%d", block);
+    
+    if (!APR_BRIGADE_EMPTY(io->input)) {
+        /* Seems something is left from a previous read, lets
+         * satisfy our caller with the data we already have. */
+        status = h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done);
+        if (status != APR_SUCCESS || done) {
+            return status;
+        }
+        apr_brigade_cleanup(io->input);
+    }
+
+    /* We only do a blocking read when we have no streams to process. So,
+     * in httpd scoreboard lingo, we are in a KEEPALIVE connection state.
+     * When reading non-blocking, we do have streams to process and update
+     * child with NULL request. That way, any current request information
+     * in the scoreboard is preserved.
+     */
+    if (block == APR_BLOCK_READ) {
+        ap_update_child_status_from_conn(io->connection->sbh, 
+                                         SERVER_BUSY_KEEPALIVE, 
+                                         io->connection);
+    }
+    else {
+        ap_update_child_status(io->connection->sbh, SERVER_BUSY_READ, NULL);
+    }
+
+    status = ap_get_brigade(io->connection->input_filters,
+                            io->input, AP_MODE_READBYTES,
+                            block, 16 * 4096);
+    switch (status) {
+        case APR_SUCCESS:
+            return h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done);
+        case APR_EOF:
+        case APR_EAGAIN:
+            break;
+        default:
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
+                          "h2_conn_io: error reading");
+            break;
+    }
+    return status;
+}
+
+static apr_status_t flush_out(apr_bucket_brigade *bb, void *ctx) 
+{
+    h2_conn_io *io = (h2_conn_io*)ctx;
+    
+    ap_update_child_status(io->connection->sbh, SERVER_BUSY_WRITE, NULL);
+    
+    apr_status_t status = ap_pass_brigade(io->connection->output_filters, bb);
+    apr_brigade_cleanup(bb);
+    return status;
+}
+
+static apr_status_t bucketeer_buffer(h2_conn_io *io) {
+    const char *data = io->buffer;
+    apr_size_t remaining = io->buflen;
+    int bcount = (int)(remaining / H2_CONN_IO_SSL_WRITE_SIZE);
+    apr_bucket *b;
+    
+    for (int i = 0; i < bcount; ++i) {
+        b = apr_bucket_transient_create(data, H2_CONN_IO_SSL_WRITE_SIZE, 
+                                        io->output->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(io->output, b);
+        data += H2_CONN_IO_SSL_WRITE_SIZE;
+        remaining -= H2_CONN_IO_SSL_WRITE_SIZE;
+    }
+    
+    if (remaining > 0) {
+        b = apr_bucket_transient_create(data, remaining, 
+                                        io->output->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(io->output, b);
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_io_write(h2_conn_io *io, 
+                              const char *buf, size_t length)
+{
+    apr_status_t status = APR_SUCCESS;
+    io->unflushed = 1;
+    
+    if (io->buffer_output) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
+                      "h2_conn_io: buffering %ld bytes", (long)length);
+        while (length > 0 && (status == APR_SUCCESS)) {
+            apr_size_t avail = io->bufsize - io->buflen;
+            if (avail <= 0) {
+                bucketeer_buffer(io);
+                status = flush_out(io->output, io);
+                io->buflen = 0;
+            }
+            else if (length > avail) {
+                memcpy(io->buffer + io->buflen, buf, avail);
+                io->buflen += avail;
+                length -= avail;
+                buf += avail;
+            }
+            else {
+                memcpy(io->buffer + io->buflen, buf, length);
+                io->buflen += length;
+                length = 0;
+                break;
+            }
+        }
+        
+    }
+    else {
+        status = apr_brigade_write(io->output, flush_out, io, buf, length);
+        if (status == APR_SUCCESS
+            || APR_STATUS_IS_ECONNABORTED(status)
+            || APR_STATUS_IS_EPIPE(status)) {
+            /* These are all fine and no reason for concern. Everything else
+             * is interesting. */
+            status = APR_SUCCESS;
+        }
+        else {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
+                          "h2_conn_io: write error");
+        }
+    }
+    
+    return status;
+}
+
+
+apr_status_t h2_conn_io_flush(h2_conn_io *io)
+{
+    if (io->unflushed) {
+        if (io->buflen > 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
+                          "h2_conn_io: flush, flushing %ld bytes", (long)io->buflen);
+            apr_bucket *b = apr_bucket_transient_create(io->buffer, io->buflen, 
+                                                        io->output->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(io->output, b);
+            io->buflen = 0;
+        }
+        /* Append flush.
+         */
+        APR_BRIGADE_INSERT_TAIL(io->output,
+                                apr_bucket_flush_create(io->output->bucket_alloc));
+        
+        /* Send it out through installed filters (TLS) to the client */
+        apr_status_t status = flush_out(io->output, io);
+        
+        if (status == APR_SUCCESS
+            || APR_STATUS_IS_ECONNABORTED(status)
+            || APR_STATUS_IS_EPIPE(status)) {
+            /* These are all fine and no reason for concern. Everything else
+             * is interesting. */
+            io->unflushed = 0;
+        }
+        else {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
+                          "h2_conn_io: flush error");
+        }
+        
+        return status;
+    }
+    return APR_SUCCESS;
+}
+
diff --git a/modules/http2/mod_h2/h2_conn_io.h b/modules/http2/mod_h2/h2_conn_io.h
new file mode 100644 (file)
index 0000000..ed2ab4c
--- /dev/null
@@ -0,0 +1,55 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_conn_io__
+#define __mod_h2__h2_conn_io__
+
+/* h2_io is the basic handler of a httpd connection. It keeps two brigades,
+ * one for input, one for output and works with the installed connection
+ * filters.
+ * The read is done via a callback function, so that input can be processed
+ * directly without copying.
+ */
+typedef struct {
+    conn_rec *connection;
+    apr_bucket_brigade *input;
+    apr_bucket_brigade *output;
+    int buffer_output;
+    
+    char *buffer;
+    apr_size_t buflen;
+    apr_size_t bufsize;
+    int unflushed;
+} h2_conn_io;
+
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c);
+void h2_conn_io_destroy(h2_conn_io *io);
+
+typedef apr_status_t (*h2_conn_io_on_read_cb)(const char *data, apr_size_t len,
+                                         apr_size_t *readlen, int *done,
+                                         void *puser);
+
+apr_status_t h2_conn_io_read(h2_conn_io *io,
+                        apr_read_type_e block,
+                        h2_conn_io_on_read_cb on_read_cb,
+                        void *puser);
+
+apr_status_t h2_conn_io_write(h2_conn_io *io,
+                         const char *buf,
+                         size_t length);
+
+apr_status_t h2_conn_io_flush(h2_conn_io *io);
+
+#endif /* defined(__mod_h2__h2_conn_io__) */
diff --git a/modules/http2/mod_h2/h2_ctx.c b/modules/http2/mod_h2/h2_ctx.c
new file mode 100644 (file)
index 0000000..f813d53
--- /dev/null
@@ -0,0 +1,100 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+
+#include "h2_private.h"
+#include "h2_task.h"
+#include "h2_ctx.h"
+#include "h2_private.h"
+
+static h2_ctx *h2_ctx_create(conn_rec *c)
+{
+    h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx));
+    AP_DEBUG_ASSERT(ctx);
+    ctx->pnego_state = H2_PNEGO_NONE;
+    ap_set_module_config(c->conn_config, &h2_module, ctx);
+    return ctx;
+}
+
+h2_ctx *h2_ctx_create_for(conn_rec *c, h2_task_env *env)
+{
+    h2_ctx *ctx = h2_ctx_create(c);
+    if (ctx) {
+        ctx->task_env = env;
+    }
+    return ctx;
+}
+
+h2_ctx *h2_ctx_get(conn_rec *c)
+{
+    h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &h2_module);
+    if (ctx == NULL) {
+        ctx = h2_ctx_create(c);
+    }
+    return ctx;
+}
+
+h2_ctx *h2_ctx_rget(request_rec *r)
+{
+    return h2_ctx_get(r->connection);
+}
+
+const char *h2_ctx_pnego_get(h2_ctx *ctx)
+{
+    return ctx? ctx->protocol : NULL;
+}
+
+void h2_ctx_pnego_set_started(h2_ctx *ctx)
+{
+    ctx->pnego_state = H2_PNEGO_STARTED;
+}
+
+h2_ctx *h2_ctx_pnego_set_done(h2_ctx *ctx, const char *proto)
+{
+    ctx->protocol = proto;
+    ctx->pnego_state = H2_PNEGO_DONE;
+    ctx->is_h2 = (proto != NULL);
+    return ctx;
+}
+
+int h2_ctx_is_task(h2_ctx *ctx)
+{
+    return ctx && !!ctx->task_env;
+}
+
+int h2_ctx_pnego_is_ongoing(h2_ctx *ctx)
+{
+    return ctx && (ctx->pnego_state == H2_PNEGO_STARTED);
+}
+
+int h2_ctx_pnego_is_done(h2_ctx *ctx)
+{
+    return ctx && (ctx->pnego_state == H2_PNEGO_DONE);
+}
+
+int h2_ctx_is_active(h2_ctx *ctx)
+{
+    return ctx && ctx->is_h2;
+}
+
+struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx)
+{
+    return ctx->task_env;
+}
diff --git a/modules/http2/mod_h2/h2_ctx.h b/modules/http2/mod_h2/h2_ctx.h
new file mode 100644 (file)
index 0000000..fc10159
--- /dev/null
@@ -0,0 +1,74 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_ctx__
+#define __mod_h2__h2_ctx__
+
+struct h2_task_env;
+struct h2_config;
+
+typedef enum {
+    H2_PNEGO_NONE,
+    H2_PNEGO_STARTED,
+    H2_PNEGO_DONE,    
+} h2_pnego_state_t;
+
+/**
+ * The h2 module context associated with a connection. 
+ *
+ * It keeps track of the different types of connections:
+ * - those from clients that use HTTP/2 protocol
+ * - those from clients that do not use HTTP/2
+ * - those created by ourself to perform work on HTTP/2 streams
+ */
+typedef struct h2_ctx {
+    int is_h2;                    /* h2 engine is used */
+    h2_pnego_state_t pnego_state; /* protocol negotiation state */
+    const char *protocol;         /* the protocol negotiated */
+    struct h2_task_env *task_env; /* the h2_task environment or NULL */
+    const char *hostname;         /* hostname negotiated via SNI, optional */
+    server_rec *server;           /* httpd server config selected. */
+    struct h2_config *config;     /* effective config in this context */
+} h2_ctx;
+
+h2_ctx *h2_ctx_get(conn_rec *c);
+h2_ctx *h2_ctx_rget(request_rec *r);
+h2_ctx *h2_ctx_create_for(conn_rec *c, struct h2_task_env *env);
+
+
+void h2_ctx_pnego_set_started(h2_ctx *ctx);
+h2_ctx *h2_ctx_pnego_set_done(h2_ctx *ctx, const char *proto);
+/**
+ * Returns != 0 iff protocol negitiation did happen, not matter
+ * what the outcome was.
+ */
+int h2_ctx_pnego_is_done(h2_ctx *ctx);
+/**
+ * Returns != 0 iff protocol negotiation has started but is not
+ * done yet.
+ */
+int h2_ctx_pnego_is_ongoing(h2_ctx *ctx);
+
+/**
+ * Get the h2 protocol negotiated for this connection, or NULL.
+ */
+const char *h2_ctx_pnego_get(h2_ctx *ctx);
+
+int h2_ctx_is_task(h2_ctx *ctx);
+int h2_ctx_is_active(h2_ctx *ctx);
+
+struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx);
+
+#endif /* defined(__mod_h2__h2_ctx__) */
diff --git a/modules/http2/mod_h2/h2_from_h1.c b/modules/http2/mod_h2/h2_from_h1.c
new file mode 100644 (file)
index 0000000..cbfa44d
--- /dev/null
@@ -0,0 +1,616 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <apr_lib.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_request.h>
+#include <util_time.h>
+
+#include "h2_private.h"
+#include "h2_response.h"
+#include "h2_from_h1.h"
+#include "h2_task.h"
+#include "h2_task_output.h"
+#include "h2_util.h"
+
+
+static void set_state(h2_from_h1 *from_h1, h2_from_h1_state_t state);
+
+h2_from_h1 *h2_from_h1_create(int stream_id, apr_pool_t *pool)
+{
+    h2_from_h1 *from_h1 = apr_pcalloc(pool, sizeof(h2_from_h1));
+    if (from_h1) {
+        from_h1->stream_id = stream_id;
+        from_h1->pool = pool;
+        from_h1->state = H2_RESP_ST_STATUS_LINE;
+        from_h1->hlines = apr_array_make(pool, 10, sizeof(char *));
+    }
+    return from_h1;
+}
+
+apr_status_t h2_from_h1_destroy(h2_from_h1 *from_h1)
+{
+    if (from_h1->response) {
+        h2_response_destroy(from_h1->response);
+        from_h1->response = NULL;
+    }
+    from_h1->bb = NULL;
+    return APR_SUCCESS;
+}
+
+h2_from_h1_state_t h2_from_h1_get_state(h2_from_h1 *from_h1)
+{
+    return from_h1->state;
+}
+
+static void set_state(h2_from_h1 *from_h1, h2_from_h1_state_t state)
+{
+    if (from_h1->state != state) {
+        from_h1->state = state;
+    }
+}
+
+h2_response *h2_from_h1_get_response(h2_from_h1 *from_h1)
+{
+    return from_h1->response;
+}
+
+static apr_status_t make_h2_headers(h2_from_h1 *from_h1, request_rec *r)
+{
+    from_h1->response = h2_response_create(from_h1->stream_id, 
+                                       from_h1->status, from_h1->hlines,
+                                       from_h1->pool);
+    if (from_h1->response == NULL) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, r->connection,
+                      "h2_from_h1(%d): unable to create resp_head",
+                      from_h1->stream_id);
+        return APR_EINVAL;
+    }
+    from_h1->content_length = from_h1->response->content_length;
+    from_h1->chunked = r->chunked;
+    
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, r->connection,
+                  "h2_from_h1(%d): converted headers, content-length: %d"
+                  ", chunked=%d",
+                  from_h1->stream_id, (int)from_h1->content_length, 
+                  (int)from_h1->chunked);
+    
+    set_state(from_h1, ((from_h1->chunked || from_h1->content_length > 0)?
+                        H2_RESP_ST_BODY : H2_RESP_ST_DONE));
+    /* We are ready to be sent to the client */
+    return APR_SUCCESS;
+}
+
+static apr_status_t parse_header(h2_from_h1 *from_h1, ap_filter_t* f, 
+                                 char *line) {
+    (void)f;
+    
+    if (line[0] == ' ' || line[0] == '\t') {
+        /* continuation line from the header before this */
+        while (line[0] == ' ' || line[0] == '\t') {
+            ++line;
+        }
+        
+        char **plast = apr_array_pop(from_h1->hlines);
+        if (plast == NULL) {
+            /* not well formed */
+            return APR_EINVAL;
+        }
+        APR_ARRAY_PUSH(from_h1->hlines, const char*) = apr_psprintf(from_h1->pool, "%s %s", *plast, line);
+    }
+    else {
+        /* new header line */
+        APR_ARRAY_PUSH(from_h1->hlines, const char*) = apr_pstrdup(from_h1->pool, line);
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t get_line(h2_from_h1 *from_h1, apr_bucket_brigade *bb,
+                             ap_filter_t* f, char *line, apr_size_t len)
+{
+    if (!from_h1->bb) {
+        from_h1->bb = apr_brigade_create(from_h1->pool, f->c->bucket_alloc);
+    }
+    else {
+        apr_brigade_cleanup(from_h1->bb);                
+    }
+    apr_status_t status = apr_brigade_split_line(from_h1->bb, bb, 
+                                                 APR_BLOCK_READ, 
+                                                 HUGE_STRING_LEN);
+    if (status == APR_SUCCESS) {
+        --len;
+        status = apr_brigade_flatten(from_h1->bb, line, &len);
+        if (status == APR_SUCCESS) {
+            /* we assume a non-0 containing line and remove
+             * trailing crlf. */
+            line[len] = '\0';
+            if (len >= 2 && !strcmp(H2_CRLF, line + len - 2)) {
+                len -= 2;
+                line[len] = '\0';
+            }
+            
+            apr_brigade_cleanup(from_h1->bb);
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                          "h2_from_h1(%d): read line: %s",
+                          from_h1->stream_id, line);
+        }
+    }
+    return status;
+}
+
+apr_status_t h2_from_h1_read_response(h2_from_h1 *from_h1, ap_filter_t* f,
+                                      apr_bucket_brigade* bb)
+{
+    apr_status_t status = APR_SUCCESS;
+    char line[HUGE_STRING_LEN];
+    
+    if ((from_h1->state == H2_RESP_ST_BODY) 
+        || (from_h1->state == H2_RESP_ST_DONE)) {
+        if (from_h1->chunked) {
+            /* The httpd core HTTP_HEADER filter has or will install the 
+             * "CHUNK" output transcode filter, which appears further down 
+             * the filter chain. We do not want it for HTTP/2.
+             * Once we successfully deinstalled it, this filter has no
+             * further function and we remove it.
+             */
+            status = ap_remove_output_filter_byhandle(f->r->output_filters, 
+                                                      "CHUNK");
+            if (status == APR_SUCCESS) {
+                ap_remove_output_filter(f);
+            }
+        }
+        
+        return ap_pass_brigade(f->next, bb);
+    }
+    
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                  "h2_from_h1(%d): read_response", from_h1->stream_id);
+    
+    while (!APR_BRIGADE_EMPTY(bb) && status == APR_SUCCESS) {
+        
+        switch (from_h1->state) {
+                
+            case H2_RESP_ST_STATUS_LINE:
+            case H2_RESP_ST_HEADERS:
+                status = get_line(from_h1, bb, f, line, sizeof(line));
+                if (status != APR_SUCCESS) {
+                    return status;
+                }
+                if (from_h1->state == H2_RESP_ST_STATUS_LINE) {
+                    /* instead of parsing, just take it directly */
+                    from_h1->status = apr_psprintf(from_h1->pool, 
+                                                   "%d", f->r->status);
+                    from_h1->state = H2_RESP_ST_HEADERS;
+                }
+                else if (line[0] == '\0') {
+                    /* end of headers, create the h2_response and
+                     * pass the rest of the brigade down the filter
+                     * chain.
+                     */
+                    status = make_h2_headers(from_h1, f->r);
+                    if (from_h1->bb) {
+                        apr_brigade_destroy(from_h1->bb);
+                        from_h1->bb = NULL;
+                    }
+                    if (!APR_BRIGADE_EMPTY(bb)) {
+                        return ap_pass_brigade(f->next, bb);
+                    }
+                }
+                else {
+                    status = parse_header(from_h1, f, line);
+                }
+                break;
+                
+            default:
+                return ap_pass_brigade(f->next, bb);
+        }
+        
+    }
+    
+    return status;
+}
+
+/* This routine is called by apr_table_do and merges all instances of
+ * the passed field values into a single array that will be further
+ * processed by some later routine.  Originally intended to help split
+ * and recombine multiple Vary fields, though it is generic to any field
+ * consisting of comma/space-separated tokens.
+ */
+static int uniq_field_values(void *d, const char *key, const char *val)
+{
+    apr_array_header_t *values;
+    char *start;
+    char *e;
+    char **strpp;
+    int  i;
+    
+    (void)key;
+    values = (apr_array_header_t *)d;
+    
+    e = apr_pstrdup(values->pool, val);
+    
+    do {
+        /* Find a non-empty fieldname */
+        
+        while (*e == ',' || apr_isspace(*e)) {
+            ++e;
+        }
+        if (*e == '\0') {
+            break;
+        }
+        start = e;
+        while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
+            ++e;
+        }
+        if (*e != '\0') {
+            *e++ = '\0';
+        }
+        
+        /* Now add it to values if it isn't already represented.
+         * Could be replaced by a ap_array_strcasecmp() if we had one.
+         */
+        for (i = 0, strpp = (char **) values->elts; i < values->nelts;
+             ++i, ++strpp) {
+            if (*strpp && strcasecmp(*strpp, start) == 0) {
+                break;
+            }
+        }
+        if (i == values->nelts) {  /* if not found */
+            *(char **)apr_array_push(values) = start;
+        }
+    } while (*e != '\0');
+    
+    return 1;
+}
+
+/*
+ * Since some clients choke violently on multiple Vary fields, or
+ * Vary fields with duplicate tokens, combine any multiples and remove
+ * any duplicates.
+ */
+static void fix_vary(request_rec *r)
+{
+    apr_array_header_t *varies;
+    
+    varies = apr_array_make(r->pool, 5, sizeof(char *));
+    
+    /* Extract all Vary fields from the headers_out, separate each into
+     * its comma-separated fieldname values, and then add them to varies
+     * if not already present in the array.
+     */
+    apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
+                 (void *) varies, r->headers_out, "Vary", NULL);
+    
+    /* If we found any, replace old Vary fields with unique-ified value */
+    
+    if (varies->nelts > 0) {
+        apr_table_setn(r->headers_out, "Vary",
+                       apr_array_pstrcat(r->pool, varies, ','));
+    }
+}
+
+/* Confirm that the status line is well-formed and matches r->status.
+ * If they don't match, a filter may have negated the status line set by a
+ * handler.
+ * Zap r->status_line if bad.
+ */
+static apr_status_t validate_status_line(request_rec *r)
+{
+    char *end;
+    
+    if (r->status_line) {
+        apr_size_t len = strlen(r->status_line);
+        if (len < 3
+            || apr_strtoi64(r->status_line, &end, 10) != r->status
+            || (end - 3) != r->status_line
+            || (len >= 4 && ! apr_isspace(r->status_line[3]))) {
+            r->status_line = NULL;
+            return APR_EGENERAL;
+        }
+        /* Since we passed the above check, we know that length three
+         * is equivalent to only a 3 digit numeric http status.
+         * RFC2616 mandates a trailing space, let's add it.
+         */
+        if (len == 3) {
+            r->status_line = apr_pstrcat(r->pool, r->status_line, " ", NULL);
+            return APR_EGENERAL;
+        }
+        return APR_SUCCESS;
+    }
+    return APR_EGENERAL;
+}
+
+static void set_basic_http_header(request_rec *r, apr_table_t *headers)
+{
+    char *date = NULL;
+    const char *proxy_date = NULL;
+    const char *server = NULL;
+    const char *us = ap_get_server_banner();
+    
+    /*
+     * keep the set-by-proxy server and date headers, otherwise
+     * generate a new server header / date header
+     */
+    if (r->proxyreq != PROXYREQ_NONE) {
+        proxy_date = apr_table_get(r->headers_out, "Date");
+        if (!proxy_date) {
+            /*
+             * proxy_date needs to be const. So use date for the creation of
+             * our own Date header and pass it over to proxy_date later to
+             * avoid a compiler warning.
+             */
+            date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
+            ap_recent_rfc822_date(date, r->request_time);
+        }
+        server = apr_table_get(r->headers_out, "Server");
+    }
+    else {
+        date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
+        ap_recent_rfc822_date(date, r->request_time);
+    }
+    
+    apr_table_setn(headers, "Date", proxy_date ? proxy_date : date );
+    apr_table_unset(r->headers_out, "Date");
+    
+    if (!server && *us) {
+        server = us;
+    }
+    if (server) {
+        apr_table_setn(headers, "Server", server);
+        apr_table_unset(r->headers_out, "Server");
+    }
+}
+
+static int copy_header(void *ctx, const char *name, const char *value)
+{
+    apr_table_t *headers = ctx;
+    
+    apr_table_addn(headers, name, value);
+    return 1;
+}
+
+static h2_response *create_response(h2_from_h1 *from_h1, request_rec *r)
+{
+    apr_status_t status = APR_SUCCESS;
+    const char *clheader;
+    const char *ctype;
+    /*
+     * Now that we are ready to send a response, we need to combine the two
+     * header field tables into a single table.  If we don't do this, our
+     * later attempts to set or unset a given fieldname might be bypassed.
+     */
+    if (!apr_is_empty_table(r->err_headers_out)) {
+        r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
+                                           r->headers_out);
+    }
+    
+    /*
+     * Remove the 'Vary' header field if the client can't handle it.
+     * Since this will have nasty effects on HTTP/1.1 caches, force
+     * the response into HTTP/1.0 mode.
+     */
+    if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) {
+        apr_table_unset(r->headers_out, "Vary");
+        r->proto_num = HTTP_VERSION(1,0);
+        apr_table_setn(r->subprocess_env, "force-response-1.0", "1");
+    }
+    else {
+        fix_vary(r);
+    }
+    
+    /*
+     * Now remove any ETag response header field if earlier processing
+     * says so (such as a 'FileETag None' directive).
+     */
+    if (apr_table_get(r->notes, "no-etag") != NULL) {
+        apr_table_unset(r->headers_out, "ETag");
+    }
+    
+    /* determine the protocol and whether we should use keepalives. */
+    status = validate_status_line(r);
+    if (!r->status_line) {
+        r->status_line = ap_get_status_line(r->status);
+    } 
+    else if (status != APR_SUCCESS) {
+        /* Status line is OK but our own reason phrase
+         * would be preferred if defined
+         */
+        const char *tmp = ap_get_status_line(r->status);
+        if (!strncmp(tmp, r->status_line, 3)) {
+            r->status_line = tmp;
+        }
+    }
+    
+    if (r->chunked) {
+        apr_table_unset(r->headers_out, "Content-Length");
+    }
+    
+    ctype = ap_make_content_type(r, r->content_type);
+    if (ctype) {
+        apr_table_setn(r->headers_out, "Content-Type", ctype);
+    }
+    
+    if (r->content_encoding) {
+        apr_table_setn(r->headers_out, "Content-Encoding",
+                       r->content_encoding);
+    }
+    
+    if (!apr_is_empty_array(r->content_languages)) {
+        int i;
+        char *token;
+        char **languages = (char **)(r->content_languages->elts);
+        const char *field = apr_table_get(r->headers_out, "Content-Language");
+        
+        while (field && (token = ap_get_list_item(r->pool, &field)) != NULL) {
+            for (i = 0; i < r->content_languages->nelts; ++i) {
+                if (!strcasecmp(token, languages[i]))
+                    break;
+            }
+            if (i == r->content_languages->nelts) {
+                *((char **) apr_array_push(r->content_languages)) = token;
+            }
+        }
+        
+        field = apr_array_pstrcat(r->pool, r->content_languages, ',');
+        apr_table_setn(r->headers_out, "Content-Language", field);
+    }
+    
+    /*
+     * Control cachability for non-cachable responses if not already set by
+     * some other part of the server configuration.
+     */
+    if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
+        char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
+        ap_recent_rfc822_date(date, r->request_time);
+        apr_table_addn(r->headers_out, "Expires", date);
+    }
+    
+    /* This is a hack, but I can't find anyway around it.  The idea is that
+     * we don't want to send out 0 Content-Lengths if it is a head request.
+     * This happens when modules try to outsmart the server, and return
+     * if they see a HEAD request.  Apache 1.3 handlers were supposed to
+     * just return in that situation, and the core handled the HEAD.  In
+     * 2.0, if a handler returns, then the core sends an EOS bucket down
+     * the filter stack, and the content-length filter computes a C-L of
+     * zero and that gets put in the headers, and we end up sending a
+     * zero C-L to the client.  We can't just remove the C-L filter,
+     * because well behaved 2.0 handlers will send their data down the stack,
+     * and we will compute a real C-L for the head request. RBB
+     */
+    if (r->header_only
+        && (clheader = apr_table_get(r->headers_out, "Content-Length"))
+        && !strcmp(clheader, "0")) {
+        apr_table_unset(r->headers_out, "Content-Length");
+    }
+    
+    apr_table_t *headers = apr_table_make(r->pool, 10);
+    
+    set_basic_http_header(r, headers);
+    if (r->status == HTTP_NOT_MODIFIED) {
+        apr_table_do((int (*)(void *, const char *, const char *)) copy_header,
+                     (void *) headers, r->headers_out,
+                     "ETag",
+                     "Content-Location",
+                     "Expires",
+                     "Cache-Control",
+                     "Vary",
+                     "Warning",
+                     "WWW-Authenticate",
+                     "Proxy-Authenticate",
+                     "Set-Cookie",
+                     "Set-Cookie2",
+                     NULL);
+    }
+    else {
+        apr_table_do((int (*)(void *, const char *, const char *)) copy_header,
+                     (void *) headers, r->headers_out, NULL);
+    }
+    
+    return h2_response_rcreate(from_h1->stream_id, r, headers, r->pool);
+}
+
+apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+    h2_task_env *env = f->ctx;
+    h2_from_h1 *from_h1 = env->output? env->output->from_h1 : NULL;
+    request_rec *r = f->r;
+    apr_bucket *b;
+    ap_bucket_error *eb = NULL;
+
+    AP_DEBUG_ASSERT(from_h1 != NULL);
+    
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                  "h2_from_h1(%d): output_filter called", from_h1->stream_id);
+    
+    if (r->header_only && env->output && from_h1->response) {
+        /* throw away any data after we have compiled the response */
+        apr_brigade_cleanup(bb);
+        return OK;
+    }
+    
+    for (b = APR_BRIGADE_FIRST(bb);
+         b != APR_BRIGADE_SENTINEL(bb);
+         b = APR_BUCKET_NEXT(b))
+    {
+        if (AP_BUCKET_IS_ERROR(b) && !eb) {
+            eb = b->data;
+            continue;
+        }
+        /*
+         * If we see an EOC bucket it is a signal that we should get out
+         * of the way doing nothing.
+         */
+        if (AP_BUCKET_IS_EOC(b)) {
+            ap_remove_output_filter(f);
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+                          "h2_from_h1(%d): eoc bucket passed", 
+                          from_h1->stream_id);
+            return ap_pass_brigade(f->next, bb);
+        }
+    }
+    
+    if (eb) {
+        int st = eb->status;
+        apr_brigade_cleanup(bb);
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+                      "h2_from_h1(%d): err bucket status=%d", 
+                      from_h1->stream_id, st);
+        ap_die(st, r);
+        return AP_FILTER_ERROR;
+    }
+    
+    from_h1->response = create_response(from_h1, r);
+    if (from_h1->response == NULL) {
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+                      "h2_from_h1(%d): unable to create response", 
+                      from_h1->stream_id);
+        return APR_ENOMEM;
+    }
+    
+    if (r->header_only) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                      "h2_from_h1(%d): header_only, cleanup output brigade", 
+                      from_h1->stream_id);
+        apr_brigade_cleanup(bb);
+        return OK;
+    }
+    
+    r->sent_bodyct = 1;         /* Whatever follows is real body stuff... */
+    
+    ap_remove_output_filter(f);
+    if (APLOGctrace1(f->c)) {
+        apr_off_t len = 0;
+        apr_brigade_length(bb, 0, &len);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                      "h2_from_h1(%d): removed header filter, passing brigade "
+                      "len=%ld", from_h1->stream_id, (long)len);
+    }
+    return ap_pass_brigade(f->next, bb);
+}
+
+void h2_from_h1_die(h2_from_h1 *from_h1, int status, request_rec *r)
+{
+    r->status = status;
+    from_h1->response = create_response(from_h1, r);
+}
diff --git a/modules/http2/mod_h2/h2_from_h1.h b/modules/http2/mod_h2/h2_from_h1.h
new file mode 100644 (file)
index 0000000..a8446fb
--- /dev/null
@@ -0,0 +1,84 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_from_h1__
+#define __mod_h2__h2_from_h1__
+
+/**
+ * h2_from_h1 parses a HTTP/1.1 response into
+ * - response status
+ * - a list of header values
+ * - a series of bytes that represent the response body alone, without
+ *   any meta data, such as inserted by chunked transfer encoding.
+ *
+ * All data is allocated from the stream memory pool. 
+ *
+ * Again, see comments in h2_request: ideally we would take the headers
+ * and status from the httpd structures instead of parsing them here, but
+ * we need to have all handlers and filters involved in request/response
+ * processing, so this seems to be the way for now.
+ */
+
+typedef enum {
+    H2_RESP_ST_STATUS_LINE, /* parsing http/1 status line */
+    H2_RESP_ST_HEADERS,     /* parsing http/1 response headers */
+    H2_RESP_ST_BODY,        /* transferring response body */
+    H2_RESP_ST_DONE         /* complete response converted */
+} h2_from_h1_state_t;
+
+struct h2_response;
+
+typedef struct h2_from_h1 h2_from_h1;
+
+struct h2_from_h1 {
+    int stream_id;
+    h2_from_h1_state_t state;
+    apr_pool_t *pool;
+    apr_bucket_brigade *bb;
+    
+    apr_size_t content_length;
+    int chunked;
+    
+    const char *status;
+    apr_array_header_t *hlines;
+    
+    struct h2_response *response;
+};
+
+
+typedef void h2_from_h1_state_change_cb(struct h2_from_h1 *resp,
+                                         h2_from_h1_state_t prevstate,
+                                         void *cb_ctx);
+
+h2_from_h1 *h2_from_h1_create(int stream_id, apr_pool_t *pool);
+
+apr_status_t h2_from_h1_destroy(h2_from_h1 *response);
+
+void h2_from_h1_set_state_change_cb(h2_from_h1 *from_h1,
+                                     h2_from_h1_state_change_cb *callback,
+                                     void *cb_ctx);
+
+apr_status_t h2_from_h1_read_response(h2_from_h1 *from_h1,
+                                      ap_filter_t* f, apr_bucket_brigade* bb);
+
+struct h2_response *h2_from_h1_get_response(h2_from_h1 *from_h1);
+
+void h2_from_h1_die(h2_from_h1 *from_h1, int status, request_rec *r);
+
+h2_from_h1_state_t h2_from_h1_get_state(h2_from_h1 *from_h1);
+
+apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb);
+
+#endif /* defined(__mod_h2__h2_from_h1__) */
diff --git a/modules/http2/mod_h2/h2_h2.c b/modules/http2/mod_h2/h2_h2.c
new file mode 100644 (file)
index 0000000..ba3460a
--- /dev/null
@@ -0,0 +1,222 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_strings.h>
+#include <apr_optional.h>
+#include <apr_optional_hooks.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+
+#include "h2_stream.h"
+#include "h2_task.h"
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_conn.h"
+#include "h2_alpn.h"
+#include "h2_h2.h"
+
+const char *h2_alpn_protos[] = {
+    "h2", "h2-16", "h2-14"
+};
+apr_size_t h2_alpn_protos_len = (sizeof(h2_alpn_protos)
+                                 / sizeof(h2_alpn_protos[0]));
+
+const char *h2_upgrade_protos[] = {
+    "h2c", "h2c-16", "h2c-14",
+};
+apr_size_t h2_upgrade_protos_len = (sizeof(h2_upgrade_protos)
+                                    / sizeof(h2_upgrade_protos[0]));
+
+const char *H2_MAGIC_TOKEN = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
+
+/*******************************************************************************
+ * The optional mod_ssl functions we need. 
+ */
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec*));
+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec*));
+
+static int (*opt_ssl_engine_disable)(conn_rec*);
+static int (*opt_ssl_is_https)(conn_rec*);
+/*******************************************************************************
+ * Hooks for processing incoming connections:
+ * - pre_conn_before_tls switches SSL off for stream connections
+ * - process_conn take over connection in case of h2
+ */
+static int h2_h2_process_conn(conn_rec* c);
+static int h2_h2_remove_timeout(conn_rec* c);
+static int h2_h2_post_read_req(request_rec *r);
+
+
+/*******************************************************************************
+ * Once per lifetime init, retrieve optional functions
+ */
+apr_status_t h2_h2_init(apr_pool_t *pool, server_rec *s)
+{
+    (void)pool;
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_h2, child_init");
+    opt_ssl_engine_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
+    opt_ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
+    
+    if (!opt_ssl_is_https) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+                     "mod_ssl does not seem to be enabled");
+    }
+    
+    return APR_SUCCESS;
+}
+
+int h2_h2_is_tls(conn_rec *c)
+{
+    return opt_ssl_is_https && opt_ssl_is_https(c);
+}
+
+int h2_tls_disable(conn_rec *c)
+{
+    if (opt_ssl_engine_disable) {
+        return opt_ssl_engine_disable(c);
+    }
+    return 0;
+}
+
+/*******************************************************************************
+ * Register various hooks
+ */
+static const char *const mod_reqtimeout[] = { "reqtimeout.c", NULL};
+
+void h2_h2_register_hooks(void)
+{
+    /* When the connection processing actually starts, we might to
+     * take over, if h2* was selected by ALPN on a TLS connection.
+     */
+    ap_hook_process_connection(h2_h2_process_conn, 
+                               NULL, NULL, APR_HOOK_FIRST);
+    /* Perform connection cleanup before the actual processing happens.
+     */
+    ap_hook_process_connection(h2_h2_remove_timeout, 
+                               mod_reqtimeout, NULL, APR_HOOK_LAST);
+    
+    ap_hook_post_read_request(h2_h2_post_read_req, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+int h2_h2_remove_timeout(conn_rec* c)
+{
+    h2_ctx *ctx = h2_ctx_get(c);
+    
+    if (h2_ctx_is_task(ctx)) {
+        /* cleanup on task connections */
+        /* we once removed the reqtimeout filter on task connections,
+         * but timeouts here might have been a side effect of other things.
+         * Ideally mod_reqtimeout would do its work on task connections
+         * as it basically is a HTTP/1.1 request/response and it's made
+         * for that.
+         * So, let the filter stay for now and see if we ever encounter
+         * unexpected timeouts on tasks again.
+         */
+        //ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout");
+    }
+    else if (h2_ctx_is_active(ctx)) {
+        /* cleanup on master h2 connections */
+        ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout");
+    }
+    
+    return DECLINED;
+}
+
+int h2_h2_process_conn(conn_rec* c)
+{
+    h2_ctx *ctx = h2_ctx_get(c);
+    h2_config *cfg = h2_config_get(c);
+    apr_bucket_brigade* temp;
+
+    if (h2_ctx_is_task(ctx)) {
+        /* out stream pseudo connection */
+        return DECLINED;
+    }
+
+    /* Protocol negoation, if started, may need some speculative reading
+     * to get triggered.
+     */
+    if (h2_ctx_pnego_is_ongoing(ctx)) {
+        temp = apr_brigade_create(c->pool, c->bucket_alloc);
+        ap_get_brigade(c->input_filters, temp,
+                       AP_MODE_SPECULATIVE, APR_BLOCK_READ, 1);
+        apr_brigade_destroy(temp);
+    }
+
+    /* If we still do not know the protocol and H2Direct is enabled, check
+     * if we receive the magic PRIamble. A client sending this on connection
+     * start should know what it is doing.
+     */
+    if (!h2_ctx_pnego_is_done(ctx) && h2_config_geti(cfg, H2_CONF_DIRECT)) {
+        apr_status_t status;
+        temp = apr_brigade_create(c->pool, c->bucket_alloc);
+        status = ap_get_brigade(c->input_filters, temp,
+                                AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
+        if (status == APR_SUCCESS) {
+            char *s = NULL;
+            apr_size_t slen;
+            
+            apr_brigade_pflatten(temp, &s, &slen, c->pool);
+            if ((slen == 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
+                h2_ctx_pnego_set_done(ctx, "h2");
+            }
+        }
+        apr_brigade_destroy(temp);
+    }
+
+    /* If "h2" was selected as protocol (by whatever mechanism), take over
+     * the connection.
+     */
+    if (h2_ctx_is_active(ctx)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                      "h2_h2, connection, h2 active");
+        
+        return h2_conn_main(c);
+    }
+    
+    return DECLINED;
+}
+
+static int h2_h2_post_read_req(request_rec *r)
+{
+    h2_ctx *ctx = h2_ctx_rget(r);
+    struct h2_task_env *env = h2_ctx_get_task(ctx);
+    if (env) {
+        /* h2_task connection for a stream, not for h2c */
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                      "adding h1_to_h2_resp output filter");
+        if (env->serialize_headers) {
+            ap_add_output_filter("H1_TO_H2_RESP", env, r, r->connection);
+        }
+        else {
+            /* replace the core http filter that formats response headers
+             * in HTTP/1 with our own that collects status and headers */
+            ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
+            ap_add_output_filter("H2_RESPONSE", env, r, r->connection);
+        }
+    }
+    return DECLINED;
+}
+
+
diff --git a/modules/http2/mod_h2/h2_h2.h b/modules/http2/mod_h2/h2_h2.h
new file mode 100644 (file)
index 0000000..2640a8c
--- /dev/null
@@ -0,0 +1,59 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_h2__
+#define __mod_h2__h2_h2__
+
+/**
+ * List of ALPN protocol identifiers that we support in ALPN/NPN 
+ * negotiations.
+ */
+extern const char *h2_alpn_protos[];
+extern apr_size_t h2_alpn_protos_len;
+
+/**
+ * List of ALPN protocol identifiers that we suport in HTTP/1 Upgrade:
+ * negotiations.
+ */
+extern const char *h2_upgrade_protos[];
+extern apr_size_t h2_upgrade_protos_len;
+
+/**
+ * The magic PRIamble of RFC 7540 that is always sent when starting
+ * a h2 communication.
+ */
+extern const char *H2_MAGIC_TOKEN;
+
+/*
+ * One time, post config intialization.
+ */
+apr_status_t h2_h2_init(apr_pool_t *pool, server_rec *s);
+
+/* Is the connection a TLS connection?
+ */
+int h2_h2_is_tls(conn_rec *c);
+
+/* Disable SSL for this connection, can only be invoked in a pre-
+ * connection hook before mod_ssl.
+ * @return != 0 iff disable worked
+ */
+int h2_tls_disable(conn_rec *c);
+
+/* Register apache hooks for h2 protocol
+ */
+void h2_h2_register_hooks(void);
+
+
+#endif /* defined(__mod_h2__h2_h2__) */
diff --git a/modules/http2/mod_h2/h2_io.c b/modules/http2/mod_h2/h2_io.c
new file mode 100644 (file)
index 0000000..b44e384
--- /dev/null
@@ -0,0 +1,157 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+
+#include "h2_private.h"
+#include "h2_io.h"
+#include "h2_response.h"
+#include "h2_util.h"
+
+h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc)
+{
+    h2_io *io = apr_pcalloc(pool, sizeof(*io));
+    if (io) {
+        io->id = id;
+        io->bbin = NULL;
+        io->bbout = apr_brigade_create(pool, bucket_alloc);
+        io->response = apr_pcalloc(pool, sizeof(h2_response));
+    }
+    return io;
+}
+
+static void h2_io_cleanup(h2_io *io)
+{
+    if (io->response) {
+        h2_response_cleanup(io->response);
+    }
+}
+
+void h2_io_destroy(h2_io *io)
+{
+    h2_io_cleanup(io);
+}
+
+int h2_io_in_has_eos_for(h2_io *io)
+{
+    return io->eos_in || (io->bbin && h2_util_has_eos(io->bbin, 0));
+}
+
+int h2_io_out_has_data(h2_io *io)
+{
+    return h2_util_bb_has_data_or_eos(io->bbout);
+}
+
+apr_size_t h2_io_out_length(h2_io *io)
+{
+    if (io->bbout) {
+        apr_off_t len = 0;
+        apr_brigade_length(io->bbout, 0, &len);
+        return (len > 0)? len : 0;
+    }
+    return 0;
+}
+
+apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, 
+                           apr_size_t maxlen)
+{
+    apr_off_t start_len = 0;
+
+    if (!io->bbin || APR_BRIGADE_EMPTY(io->bbin)) {
+        return io->eos_in? APR_EOF : APR_EAGAIN;
+    }
+    
+    apr_brigade_length(bb, 1, &start_len);
+    apr_bucket *last = APR_BRIGADE_LAST(bb);
+    apr_status_t status = h2_util_move(bb, io->bbin, maxlen, 0, 
+                                       NULL, "h2_io_in_read");
+    if (status == APR_SUCCESS) {
+        apr_bucket *nlast = APR_BRIGADE_LAST(bb);
+        apr_off_t end_len = 0;
+        apr_brigade_length(bb, 1, &end_len);
+        if (last == nlast) {
+            return APR_EAGAIN;
+        }
+        io->input_consumed += (end_len - start_len);
+    }
+    return status;
+}
+
+apr_status_t h2_io_in_write(h2_io *io, apr_bucket_brigade *bb)
+{
+    if (io->eos_in) {
+        return APR_EOF;
+    }
+    io->eos_in = h2_util_has_eos(bb, 0);
+    if (!APR_BRIGADE_EMPTY(bb)) {
+        if (!io->bbin) {
+            io->bbin = apr_brigade_create(io->bbout->p, 
+                                          io->bbout->bucket_alloc);
+        }
+        return h2_util_move(io->bbin, bb, 0, 0, NULL, "h2_io_in_write");
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_io_in_close(h2_io *io)
+{
+    if (io->bbin) {
+        APR_BRIGADE_INSERT_TAIL(io->bbin, 
+                                apr_bucket_eos_create(io->bbin->bucket_alloc));
+    }
+    io->eos_in = 1;
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_io_out_read(h2_io *io, char *buffer, 
+                            apr_size_t *plen, int *peos)
+{
+    if (buffer == NULL) {
+        /* just checking length available */
+        return h2_util_bb_avail(io->bbout, plen, peos);
+    }
+    
+    return h2_util_bb_read(io->bbout, buffer, plen, peos);
+}
+
+apr_status_t h2_io_out_readx(h2_io *io,  
+                             h2_io_data_cb *cb, void *ctx, 
+                             apr_size_t *plen, int *peos)
+{
+    if (cb == NULL) {
+        /* just checking length available */
+        return h2_util_bb_avail(io->bbout, plen, peos);
+    }
+    return h2_util_bb_readx(io->bbout, cb, ctx, plen, peos);
+}
+
+apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
+                             apr_size_t maxlen)
+{
+    return h2_util_move(io->bbout, bb, maxlen, 0, NULL, "h2_io_out_write");
+}
+
+
+apr_status_t h2_io_out_close(h2_io *io)
+{
+    APR_BRIGADE_INSERT_TAIL(io->bbout, 
+                            apr_bucket_eos_create(io->bbout->bucket_alloc));
+    return APR_SUCCESS;
+}
diff --git a/modules/http2/mod_h2/h2_io.h b/modules/http2/mod_h2/h2_io.h
new file mode 100644 (file)
index 0000000..119cd8a
--- /dev/null
@@ -0,0 +1,127 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_io__
+#define __mod_h2__h2_io__
+
+struct h2_response;
+struct apr_thread_cond_t;
+struct h2_task;
+
+
+typedef apr_status_t h2_io_data_cb(void *ctx, 
+                                   const char *data, apr_size_t len);
+
+
+typedef struct h2_io h2_io;
+
+struct h2_io {
+    int id;                      /* stream identifier */
+    apr_bucket_brigade *bbin;    /* input data for stream */
+    int eos_in;
+    int task_done;
+    
+    apr_size_t input_consumed;   /* how many bytes have been read */
+    struct apr_thread_cond_t *input_arrived; /* block on reading */
+    
+    apr_bucket_brigade *bbout;   /* output data from stream */
+    struct apr_thread_cond_t *output_drained; /* block on writing */
+    
+    struct h2_response *response;/* submittable response created */
+};
+
+/*******************************************************************************
+ * Object lifecycle and information.
+ ******************************************************************************/
+
+/**
+ * Creates a new h2_io for the given stream id. 
+ */
+h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc);
+
+/**
+ * Frees any resources hold by the h2_io instance. 
+ */
+void h2_io_destroy(h2_io *io);
+
+/**
+ * The input data is completely queued. Blocked reads will return immediately
+ * and give either data or EOF.
+ */
+int h2_io_in_has_eos_for(h2_io *io);
+/**
+ * Output data is available.
+ */
+int h2_io_out_has_data(h2_io *io);
+
+/*******************************************************************************
+ * Input handling of streams.
+ ******************************************************************************/
+/**
+ * Reads the next bucket from the input. Returns APR_EAGAIN if none
+ * is currently available, APR_EOF if end of input has been reached.
+ */
+apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, 
+                           apr_size_t maxlen);
+
+/**
+ * Appends given bucket to the input.
+ */
+apr_status_t h2_io_in_write(h2_io *io, apr_bucket_brigade *bb);
+
+/**
+ * Closes the input. After existing data has been read, APR_EOF will
+ * be returned.
+ */
+apr_status_t h2_io_in_close(h2_io *io);
+
+/*******************************************************************************
+ * Output handling of streams.
+ ******************************************************************************/
+
+/**
+ * Read a bucket from the output head. Return APR_EAGAIN if non is available,
+ * APR_EOF if none available and output has been closed. 
+ * May be called with buffer == NULL in order to find out how much data
+ * is available.
+ * @param io the h2_io to read output from
+ * @param buffer the buffer to copy the data to, may be NULL
+ * @param plen the requested max len, set to amount of data on return
+ * @param peos != 0 iff the end of stream has been reached
+ */
+apr_status_t h2_io_out_read(h2_io *io, char *buffer, 
+                            apr_size_t *plen, int *peos);
+
+apr_status_t h2_io_out_readx(h2_io *io,  
+                             h2_io_data_cb *cb, void *ctx, 
+                             apr_size_t *plen, int *peos);
+
+apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
+                             apr_size_t maxlen);
+
+/**
+ * Closes the input. After existing data has been read, APR_EOF will
+ * be returned.
+ */
+apr_status_t h2_io_out_close(h2_io *io);
+
+/**
+ * Gives the overall length of the data that is currently queued for
+ * output.
+ */
+apr_size_t h2_io_out_length(h2_io *io);
+
+
+#endif /* defined(__mod_h2__h2_io__) */
diff --git a/modules/http2/mod_h2/h2_io_set.c b/modules/http2/mod_h2/h2_io_set.c
new file mode 100644 (file)
index 0000000..d87574c
--- /dev/null
@@ -0,0 +1,169 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_connection.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_io.h"
+#include "h2_io_set.h"
+
+#define h2_io_IDX(list, i) ((h2_io**)(list)->elts)[i]
+
+struct h2_io_set {
+    apr_array_header_t *list;
+};
+
+h2_io_set *h2_io_set_create(apr_pool_t *pool)
+{
+    h2_io_set *sp = apr_pcalloc(pool, sizeof(h2_io_set));
+    if (sp) {
+        sp->list = apr_array_make(pool, 100, sizeof(h2_io*));
+        if (!sp->list) {
+            return NULL;
+        }
+    }
+    return sp;
+}
+
+void h2_io_set_destroy(h2_io_set *sp)
+{
+    for (int i = 0; i < sp->list->nelts; ++i) {
+        h2_io *io = h2_io_IDX(sp->list, i);
+        h2_io_destroy(io);
+    }
+    sp->list->nelts = 0;
+}
+
+static int h2_stream_id_cmp(const void *s1, const void *s2)
+{
+    h2_io **pio1 = (h2_io **)s1;
+    h2_io **pio2 = (h2_io **)s2;
+    return (*pio1)->id - (*pio2)->id;
+}
+
+h2_io *h2_io_set_get(h2_io_set *sp, int stream_id)
+{
+    /* we keep the array sorted by id, so lookup can be done
+     * by bsearch.
+     */
+    h2_io key = { stream_id, NULL, 0, 0, 0, NULL, NULL, NULL, NULL };
+    h2_io *pkey = &key;
+    h2_io **ps = bsearch(&pkey, sp->list->elts, sp->list->nelts, 
+                         sp->list->elt_size, h2_stream_id_cmp);
+    return ps? *ps : NULL;
+}
+
+h2_io *h2_io_set_get_highest_prio(h2_io_set *set)
+{
+    h2_io *highest = NULL;
+    for (int i = 0; i < set->list->nelts; ++i) {
+        h2_io *io = h2_io_IDX(set->list, i);
+        if (!highest /*|| io-prio even higher */ ) {
+            highest = io;
+        }
+    }
+    return highest;
+}
+
+static void h2_io_set_sort(h2_io_set *sp)
+{
+    qsort(sp->list->elts, sp->list->nelts, sp->list->elt_size, 
+          h2_stream_id_cmp);
+}
+
+apr_status_t h2_io_set_add(h2_io_set *sp, h2_io *io)
+{
+    h2_io *existing = h2_io_set_get(sp, io->id);
+    if (!existing) {
+        APR_ARRAY_PUSH(sp->list, h2_io*) = io;
+        /* Normally, streams get added in ascending order if id. We
+         * keep the array sorted, so we just need to check of the newly
+         * appended stream has a lower id than the last one. if not,
+         * sorting is not necessary.
+         */
+        int last = sp->list->nelts - 1;
+        if (last > 0 
+            && (h2_io_IDX(sp->list, last)->id 
+                < h2_io_IDX(sp->list, last-1)->id)) {
+                h2_io_set_sort(sp);
+            }
+    }
+    return APR_SUCCESS;
+}
+
+h2_io *h2_io_set_remove(h2_io_set *sp, h2_io *io)
+{
+    for (int i = 0; i < sp->list->nelts; ++i) {
+        h2_io *e = h2_io_IDX(sp->list, i);
+        if (e == io) {
+            --sp->list->nelts;
+            int n = sp->list->nelts - i;
+            if (n > 0) {
+                /* Close the hole in the array by moving the upper
+                 * parts down one step.
+                 */
+                h2_io **selts = (h2_io**)sp->list->elts;
+                memmove(selts+i, selts+i+1, n * sizeof(h2_io*));
+            }
+            return e;
+        }
+    }
+    return NULL;
+}
+
+void h2_io_set_destroy_all(h2_io_set *sp)
+{
+    for (int i = 0; i < sp->list->nelts; ++i) {
+        h2_io *io = h2_io_IDX(sp->list, i);
+        h2_io_destroy(io);
+    }
+    sp->list->nelts = 0;
+}
+
+void h2_io_set_remove_all(h2_io_set *sp)
+{
+    sp->list->nelts = 0;
+}
+
+int h2_io_set_is_empty(h2_io_set *sp)
+{
+    AP_DEBUG_ASSERT(sp);
+    return sp->list->nelts == 0;
+}
+
+void h2_io_set_iter(h2_io_set *sp,
+                        h2_io_set_iter_fn *iter, void *ctx)
+{
+    for (int i = 0; i < sp->list->nelts; ++i) {
+        h2_io *s = h2_io_IDX(sp->list, i);
+        if (!iter(ctx, s)) {
+            break;
+        }
+    }
+}
+
+apr_size_t h2_io_set_size(h2_io_set *sp)
+{
+    return sp->list->nelts;
+}
+
diff --git a/modules/http2/mod_h2/h2_io_set.h b/modules/http2/mod_h2/h2_io_set.h
new file mode 100644 (file)
index 0000000..a9c6546
--- /dev/null
@@ -0,0 +1,47 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_io_set__
+#define __mod_h2__h2_io_set__
+
+struct h2_io;
+
+/**
+ * A set of h2_io instances. Allows lookup by stream id
+ * and other criteria.
+ */
+typedef struct h2_io_set h2_io_set;
+
+h2_io_set *h2_io_set_create(apr_pool_t *pool);
+
+void h2_io_set_destroy(h2_io_set *set);
+
+apr_status_t h2_io_set_add(h2_io_set *set, struct h2_io *io);
+h2_io *h2_io_set_get(h2_io_set *set, int stream_id);
+h2_io *h2_io_set_get_highest_prio(h2_io_set *set);
+h2_io *h2_io_set_remove(h2_io_set *set, struct h2_io *io);
+
+void h2_io_set_remove_all(h2_io_set *set);
+void h2_io_set_destroy_all(h2_io_set *set);
+int h2_io_set_is_empty(h2_io_set *set);
+apr_size_t h2_io_set_size(h2_io_set *set);
+
+
+typedef int h2_io_set_iter_fn(void *ctx, struct h2_io *io);
+
+void h2_io_set_iter(h2_io_set *set,
+                           h2_io_set_iter_fn *iter, void *ctx);
+
+#endif /* defined(__mod_h2__h2_io_set__) */
diff --git a/modules/http2/mod_h2/h2_mplx.c b/modules/http2/mod_h2/h2_mplx.c
new file mode 100644 (file)
index 0000000..be477e1
--- /dev/null
@@ -0,0 +1,788 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#include <apr_atomic.h>
+#include <apr_thread_mutex.h>
+#include <apr_thread_cond.h>
+#include <apr_strings.h>
+#include <apr_time.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_config.h"
+#include "h2_conn.h"
+#include "h2_io.h"
+#include "h2_io_set.h"
+#include "h2_response.h"
+#include "h2_mplx.h"
+#include "h2_stream.h"
+#include "h2_stream_set.h"
+#include "h2_task.h"
+#include "h2_task_input.h"
+#include "h2_task_output.h"
+#include "h2_task_queue.h"
+#include "h2_workers.h"
+
+
+static int is_aborted(h2_mplx *m, apr_status_t *pstatus) {
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        *pstatus = APR_ECONNABORTED;
+        return 1;
+    }
+    return 0;
+}
+
+static void have_out_data_for(h2_mplx *m, int stream_id);
+
+static void h2_mplx_destroy(h2_mplx *m)
+{
+    AP_DEBUG_ASSERT(m);
+    m->aborted = 1;
+    if (m->q) {
+        h2_tq_destroy(m->q);
+        m->q = NULL;
+    }
+    if (m->ready_ios) {
+        h2_io_set_destroy(m->ready_ios);
+        m->ready_ios = NULL;
+    }
+    if (m->stream_ios) {
+        h2_io_set_destroy(m->stream_ios);
+        m->stream_ios = NULL;
+    }
+    
+    if (m->lock) {
+        apr_thread_mutex_destroy(m->lock);
+        m->lock = NULL;
+    }
+    
+    if (m->pool) {
+        apr_pool_destroy(m->pool);
+    }
+}
+
+/**
+ * A h2_mplx needs to be thread-safe *and* if will be called by
+ * the h2_session thread *and* the h2_worker threads. Therefore:
+ * - calls are protected by a mutex lock, m->lock
+ * - the pool needs its own allocator, since apr_allocator_t are 
+ *   not re-entrant. The separate allocator works without a 
+ *   separate lock since we already protect h2_mplx itself.
+ *   Since HTTP/2 connections can be expected to live longer than
+ *   their HTTP/1 cousins, the separate allocator seems to work better
+ *   than protecting a shared h2_session one with an own lock.
+ */
+h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, h2_workers *workers)
+{
+    apr_status_t status = APR_SUCCESS;
+    h2_config *conf = h2_config_get(c);
+    AP_DEBUG_ASSERT(conf);
+    
+    apr_allocator_t *allocator = NULL;
+    status = apr_allocator_create(&allocator);
+    if (status != APR_SUCCESS) {
+        return NULL;
+    }
+
+    h2_mplx *m = apr_pcalloc(parent, sizeof(h2_mplx));
+    if (m) {
+        m->id = c->id;
+        APR_RING_ELEM_INIT(m, link);
+        apr_atomic_set32(&m->refs, 1);
+        m->c = c;
+        apr_pool_create_ex(&m->pool, parent, NULL, allocator);
+        if (!m->pool) {
+            return NULL;
+        }
+        apr_allocator_owner_set(allocator, m->pool);
+        
+        status = apr_thread_mutex_create(&m->lock, APR_THREAD_MUTEX_DEFAULT,
+                                         m->pool);
+        if (status != APR_SUCCESS) {
+            h2_mplx_destroy(m);
+            return NULL;
+        }
+        
+        m->bucket_alloc = apr_bucket_alloc_create(m->pool);
+        
+        m->q = h2_tq_create(m->id, m->pool);
+        m->stream_ios = h2_io_set_create(m->pool);
+        m->ready_ios = h2_io_set_create(m->pool);
+        m->closed = h2_stream_set_create(m->pool);
+        m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM);
+        m->workers = workers;
+    }
+    return m;
+}
+
+#define REF_COUNT_ATOMIC    1
+
+static void reference(h2_mplx *m)
+{
+    apr_atomic_inc32(&m->refs);
+}
+
+static void release(h2_mplx *m)
+{
+    if (!apr_atomic_dec32(&m->refs)) {
+        if (m->join_wait) {
+            apr_thread_cond_signal(m->join_wait);
+        }
+    }
+}
+
+void h2_mplx_reference(h2_mplx *m)
+{
+    if (REF_COUNT_ATOMIC) {
+        reference(m);
+    }
+    else {
+        apr_status_t status = apr_thread_mutex_lock(m->lock);
+        if (APR_SUCCESS == status) {
+            reference(m);
+            apr_thread_mutex_unlock(m->lock);
+        }
+    }
+}
+void h2_mplx_release(h2_mplx *m)
+{
+    if (REF_COUNT_ATOMIC) {
+        release(m);
+    }
+    else {
+        apr_status_t status = apr_thread_mutex_lock(m->lock);
+        if (APR_SUCCESS == status) {
+            release(m);
+            apr_thread_mutex_unlock(m->lock);
+        }
+    }
+}
+
+static void workers_register(h2_mplx *m) {
+    /* Initially, there was ref count increase for this as well, but
+     * this is not needed, even harmful.
+     * h2_workers is only a hub for all the h2_worker instances.
+     * At the end-of-life of this h2_mplx, we always unregister at
+     * the workers. The thing to manage are all the h2_worker instances
+     * out there. Those may hold a reference to this h2_mplx and we cannot
+     * call them to unregister.
+     * 
+     * Therefore: ref counting for h2_workers in not needed, ref counting
+     * for h2_worker using this is critical.
+     */
+    h2_workers_register(m->workers, m);
+}
+
+static void workers_unregister(h2_mplx *m) {
+    h2_workers_unregister(m->workers, m);
+}
+
+apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
+{
+    workers_unregister(m);
+
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        int attempts = 0;
+        
+        release(m);
+        while (apr_atomic_read32(&m->refs) > 0) {
+            m->join_wait = wait;
+            ap_log_cerror(APLOG_MARK, (attempts? APLOG_INFO : APLOG_DEBUG), 
+                          0, m->c,
+                          "h2_mplx(%ld): release_join, refs=%d, waiting...", 
+                          m->id, m->refs);
+            apr_thread_cond_timedwait(wait, m->lock, apr_time_from_sec(10));
+            if (++attempts >= 6) {
+                ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c,
+                              "h2_mplx(%ld): join attempts exhausted, refs=%d", 
+                              m->id, m->refs);
+                break;
+            }
+        }
+        if (m->join_wait) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
+                          "h2_mplx(%ld): release_join -> destroy", m->id);
+        }
+        m->join_wait = NULL;
+        apr_thread_mutex_unlock(m->lock);
+        h2_mplx_destroy(m);
+    }
+    return status;
+}
+
+void h2_mplx_abort(h2_mplx *m)
+{
+    AP_DEBUG_ASSERT(m);
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        m->aborted = 1;
+        h2_io_set_destroy_all(m->stream_ios);
+        apr_thread_mutex_unlock(m->lock);
+    }
+    workers_unregister(m);
+}
+
+
+h2_stream *h2_mplx_open_io(h2_mplx *m, int stream_id)
+{
+    h2_stream *stream = NULL;
+
+    if (m->aborted) {
+        return NULL;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        apr_pool_t *stream_pool = m->spare_pool;
+        
+        if (!stream_pool) {
+            apr_pool_create(&stream_pool, m->pool);
+        }
+        else {
+            m->spare_pool = NULL;
+        }
+        
+        stream = h2_stream_create(stream_id, stream_pool, m);
+        stream->state = H2_STREAM_ST_OPEN;
+        
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (!io) {
+            io = h2_io_create(stream_id, stream_pool, m->bucket_alloc);
+            h2_io_set_add(m->stream_ios, io);
+        }
+        status = io? APR_SUCCESS : APR_ENOMEM;
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return stream;
+}
+
+static void stream_destroy(h2_mplx *m, h2_stream *stream, h2_io *io)
+{
+    apr_pool_t *pool = h2_stream_detach_pool(stream);
+    if (pool) {
+        apr_pool_clear(pool);
+        if (m->spare_pool) {
+            apr_pool_destroy(m->spare_pool);
+        }
+        m->spare_pool = pool;
+    }
+    h2_stream_destroy(stream);
+    if (io) {
+        h2_io_set_remove(m->stream_ios, io);
+        h2_io_destroy(io);
+    }
+}
+
+apr_status_t h2_mplx_cleanup_stream(h2_mplx *m, h2_stream *stream)
+{
+    AP_DEBUG_ASSERT(m);
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream->id);
+        if (!io || io->task_done) {
+            /* No more io or task already done -> cleanup immediately */
+            stream_destroy(m, stream, io);
+        }
+        else {
+            /* Add stream to closed set for cleanup when task is done */
+            h2_stream_set_add(m->closed, stream);
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+void h2_mplx_task_done(h2_mplx *m, int stream_id)
+{
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_stream *stream = h2_stream_set_get(m->closed, stream_id);
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
+                      "h2_mplx(%ld): task(%d) done", m->id, stream_id);
+        if (stream) {
+            /* stream was already closed by main connection and is in 
+             * zombie state. Now that the task is done with it, we
+             * can free its resources. */
+            h2_stream_set_remove(m->closed, stream);
+            stream_destroy(m, stream, io);
+        }
+        else if (io) {
+            /* main connection has not finished stream. Mark task as done
+             * so that eventual cleanup can start immediately. */
+            io->task_done = 1;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+}
+
+apr_status_t h2_mplx_in_read(h2_mplx *m, apr_read_type_e block,
+                             int stream_id, apr_bucket_brigade *bb,
+                             struct apr_thread_cond_t *iowait)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (io) {
+            io->input_arrived = iowait;
+            status = h2_io_in_read(io, bb, 0);
+            while (status == APR_EAGAIN 
+                   && !is_aborted(m, &status)
+                   && block == APR_BLOCK_READ) {
+                apr_thread_cond_wait(io->input_arrived, m->lock);
+                status = h2_io_in_read(io, bb, 0);
+            }
+            io->input_arrived = NULL;
+        }
+        else {
+            status = APR_EOF;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+apr_status_t h2_mplx_in_write(h2_mplx *m, int stream_id, 
+                              apr_bucket_brigade *bb)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (io) {
+            status = h2_io_in_write(io, bb);
+            if (io->input_arrived) {
+                apr_thread_cond_signal(io->input_arrived);
+            }
+        }
+        else {
+            status = APR_EOF;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+apr_status_t h2_mplx_in_close(h2_mplx *m, int stream_id)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (io) {
+            status = h2_io_in_close(io);
+            if (io->input_arrived) {
+                apr_thread_cond_signal(io->input_arrived);
+            }
+        }
+        else {
+            status = APR_ECONNABORTED;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+typedef struct {
+    h2_mplx_consumed_cb *cb;
+    void *cb_ctx;
+    int streams_updated;
+} update_ctx;
+
+static int update_window(void *ctx, h2_io *io)
+{
+    if (io->input_consumed) {
+        update_ctx *uctx = (update_ctx*)ctx;
+        uctx->cb(uctx->cb_ctx, io->id, io->input_consumed);
+        io->input_consumed = 0;
+        ++uctx->streams_updated;
+    }
+    return 1;
+}
+
+apr_status_t h2_mplx_in_update_windows(h2_mplx *m, 
+                                       h2_mplx_consumed_cb *cb, void *cb_ctx)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        update_ctx ctx = { cb, cb_ctx, 0 };
+        status = APR_EAGAIN;
+        h2_io_set_iter(m->stream_ios, update_window, &ctx);
+        
+        if (ctx.streams_updated) {
+            status = APR_SUCCESS;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+apr_status_t h2_mplx_out_read(h2_mplx *m, int stream_id, 
+                              char *buffer, apr_size_t *plen, int *peos)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (io) {
+            status = h2_io_out_read(io, buffer, plen, peos);
+            if (status == APR_SUCCESS && io->output_drained) {
+                apr_thread_cond_signal(io->output_drained);
+            }
+        }
+        else {
+            status = APR_ECONNABORTED;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, 
+                               h2_io_data_cb *cb, void *ctx, 
+                               apr_size_t *plen, int *peos)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (io) {
+            status = h2_io_out_readx(io, cb, ctx, plen, peos);
+            if (status == APR_SUCCESS && io->output_drained) {
+                apr_thread_cond_signal(io->output_drained);
+            }
+        }
+        else {
+            status = APR_ECONNABORTED;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return NULL;
+    }
+    h2_stream *stream = NULL;
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get_highest_prio(m->ready_ios);
+        if (io) {
+            h2_response *response = io->response;
+            h2_io_set_remove(m->ready_ios, io);
+            
+            stream = h2_stream_set_get(streams, response->stream_id);
+            if (stream) {
+                h2_stream_set_response(stream, response, io->bbout);
+                if (io->output_drained) {
+                    apr_thread_cond_signal(io->output_drained);
+                }
+            }
+            else {
+                ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_NOTFOUND, m->c,
+                              "h2_mplx(%ld): stream for response %d",
+                              m->id, response->stream_id);
+            }
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return stream;
+}
+
+static apr_status_t out_write(h2_mplx *m, h2_io *io, 
+                              ap_filter_t* f, apr_bucket_brigade *bb,
+                              struct apr_thread_cond_t *iowait)
+{
+    apr_status_t status = APR_SUCCESS;
+    /* We check the memory footprint queued for this stream_id
+     * and block if it exceeds our configured limit.
+     * We will not split buckets to enforce the limit to the last
+     * byte. After all, the bucket is already in memory.
+     */
+    while (!APR_BRIGADE_EMPTY(bb) 
+           && (status == APR_SUCCESS)
+           && !is_aborted(m, &status)) {
+        
+        status = h2_io_out_write(io, bb, m->stream_max_mem);
+        
+        /* Wait for data to drain until there is room again */
+        while (!APR_BRIGADE_EMPTY(bb) 
+               && iowait
+               && status == APR_SUCCESS
+               && (m->stream_max_mem <= h2_io_out_length(io))
+               && !is_aborted(m, &status)) {
+            io->output_drained = iowait;
+            if (f) {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                              "h2_mplx(%ld-%d): waiting for out drain", 
+                              m->id, io->id);
+            }
+            apr_thread_cond_wait(io->output_drained, m->lock);
+            io->output_drained = NULL;
+        }
+    }
+    apr_brigade_cleanup(bb);
+    return status;
+}
+
+static apr_status_t out_open(h2_mplx *m, int stream_id, h2_response *response,
+                             ap_filter_t* f, apr_bucket_brigade *bb,
+                             struct apr_thread_cond_t *iowait)
+{
+    apr_status_t status = APR_SUCCESS;
+    
+    h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+    if (io) {
+        if (f) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
+                          "h2_mplx(%ld-%d): open response: %s",
+                          m->id, stream_id, response->headers->status);
+        }
+        
+        h2_response_copy(io->response, response);
+        h2_io_set_add(m->ready_ios, io);
+        if (bb) {
+            status = out_write(m, io, f, bb, iowait);
+        }
+        have_out_data_for(m, stream_id);
+    }
+    else {
+        status = APR_ECONNABORTED;
+    }
+    return status;
+}
+
+apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_response *response,
+                              ap_filter_t* f, apr_bucket_brigade *bb,
+                              struct apr_thread_cond_t *iowait)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        status = out_open(m, stream_id, response, f, bb, iowait);
+        if (m->aborted) {
+            return APR_ECONNABORTED;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+
+apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, 
+                               ap_filter_t* f, apr_bucket_brigade *bb,
+                               struct apr_thread_cond_t *iowait)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        if (!m->aborted) {
+            h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+            if (io) {
+                status = out_write(m, io, f, bb, iowait);
+                have_out_data_for(m, stream_id);
+                if (m->aborted) {
+                    return APR_ECONNABORTED;
+                }
+            }
+            else {
+                status = APR_ECONNABORTED;
+            }
+        }
+        
+        if (m->lock) {
+            apr_thread_mutex_unlock(m->lock);
+        }
+    }
+    return status;
+}
+
+apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        if (!m->aborted) {
+            h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+            if (io) {
+                if (!io->response->headers) {
+                    /* In case a close comes before a response was created,
+                     * insert an error one so that our streams can properly
+                     * reset.
+                     */
+                    h2_response *r = h2_response_create(stream_id, 
+                                                        "500", NULL, m->pool);
+                    status = out_open(m, stream_id, r, NULL, NULL, NULL);
+                }
+                status = h2_io_out_close(io);
+                have_out_data_for(m, stream_id);
+                if (m->aborted) {
+                    /* if we were the last output, the whole session might
+                     * have gone down in the meantime.
+                     */
+                    return APR_SUCCESS;
+                }
+            }
+            else {
+                status = APR_ECONNABORTED;
+            }
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+int h2_mplx_in_has_eos_for(h2_mplx *m, int stream_id)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return 0;
+    }
+    int has_eos = 0;
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (io) {
+            has_eos = h2_io_in_has_eos_for(io);
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return has_eos;
+}
+
+int h2_mplx_out_has_data_for(h2_mplx *m, int stream_id)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return 0;
+    }
+    int has_data = 0;
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
+        if (io) {
+            has_data = h2_io_out_has_data(io);
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return has_data;
+}
+
+apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout,
+                                 apr_thread_cond_t *iowait)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        m->added_output = iowait;
+        status = apr_thread_cond_timedwait(m->added_output, m->lock, timeout);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
+                      "h2_mplx(%ld): trywait on data for %f ms)",
+                      m->id, timeout/1000.0);
+        m->added_output = NULL;
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
+static void have_out_data_for(h2_mplx *m, int stream_id)
+{
+    (void)stream_id;
+    AP_DEBUG_ASSERT(m);
+    if (m->added_output) {
+        apr_thread_cond_signal(m->added_output);
+    }
+}
+
+apr_status_t h2_mplx_do_task(h2_mplx *m, struct h2_task *task)
+{
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        /* TODO: needs to sort queue by priority */
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
+                      "h2_mplx: do task(%s)", task->id);
+        h2_tq_append(m->q, task);
+        apr_thread_mutex_unlock(m->lock);
+    }
+    workers_register(m);
+    return status;
+}
+
+h2_task *h2_mplx_pop_task(h2_mplx *m, int *has_more)
+{
+    h2_task *task = NULL;
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        *has_more = 0;
+        return NULL;
+    }
+    apr_status_t status = apr_thread_mutex_lock(m->lock);
+    if (APR_SUCCESS == status) {
+        task = h2_tq_pop_first(m->q);
+        if (task) {
+            h2_task_set_started(task);
+        }
+        *has_more = !h2_tq_empty(m->q);
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return task;
+}
+
diff --git a/modules/http2/mod_h2/h2_mplx.h b/modules/http2/mod_h2/h2_mplx.h
new file mode 100644 (file)
index 0000000..35c41a6
--- /dev/null
@@ -0,0 +1,322 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_mplx__
+#define __mod_h2__h2_mplx__
+
+/**
+ * The stream multiplexer. It pushes buckets from the connection
+ * thread to the stream task threads and vice versa. It's thread-safe
+ * to use.
+ *
+ * There is one h2_mplx instance for each h2_session, which sits on top
+ * of a particular httpd conn_rec. Input goes from the connection to
+ * the stream tasks. Output goes from the stream tasks to the connection,
+ * e.g. the client.
+ *
+ * For each stream, there can be at most "H2StreamMaxMemSize" output bytes
+ * queued in the multiplexer. If a task thread tries to write more
+ * data, it is blocked until space becomes available.
+ *
+ * Writing input is never blocked. In order to use flow control on the input,
+ * the mplx can be polled for input data consumption.
+ */
+
+struct apr_pool_t;
+struct apr_thread_mutex_t;
+struct apr_thread_cond_t;
+struct h2_config;
+struct h2_response;
+struct h2_task;
+struct h2_stream;
+struct h2_io_set;
+struct apr_thread_cond_t;
+struct h2_workers;
+struct h2_stream_set;
+struct h2_task_queue;
+
+#include "h2_io.h"
+
+typedef struct h2_mplx h2_mplx;
+
+struct h2_mplx {
+    long id;
+    APR_RING_ENTRY(h2_mplx) link;
+    volatile apr_uint32_t refs;
+    conn_rec *c;
+    apr_pool_t *pool;
+    apr_bucket_alloc_t *bucket_alloc;
+
+    struct h2_task_queue *q;
+    struct h2_io_set *stream_ios;
+    struct h2_io_set *ready_ios;
+    
+    apr_thread_mutex_t *lock;
+    struct apr_thread_cond_t *added_output;
+    struct apr_thread_cond_t *join_wait;
+    
+    int aborted;
+    apr_size_t stream_max_mem;
+    
+    apr_pool_t *spare_pool;           /* spare pool, ready for next stream */
+    struct h2_stream_set *closed;     /* streams closed, but task ongoing */
+    struct h2_workers *workers;
+};
+
+/*******************************************************************************
+ * Object lifecycle and information.
+ ******************************************************************************/
+
+/**
+ * Create the multiplexer for the given HTTP2 session. 
+ * Implicitly has reference count 1.
+ */
+h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *master, 
+                        struct h2_workers *workers);
+
+/**
+ * Increase the reference counter of this mplx.
+ */
+void h2_mplx_reference(h2_mplx *m);
+
+/**
+ * Decreases the reference counter of this mplx.
+ */
+void h2_mplx_release(h2_mplx *m);
+/**
+ * Decreases the reference counter of this mplx and waits for it
+ * to reached 0, destroy the mplx afterwards.
+ * This is to be called from the thread that created the mplx in
+ * the first place.
+ * @param m the mplx to be released and destroyed
+ * @param wait condition var to wait on for ref counter == 0
+ */ 
+apr_status_t h2_mplx_release_and_join(h2_mplx *m, struct apr_thread_cond_t *wait);
+
+/**
+ * Aborts the multiplexer. It will answer all future invocation with
+ * APR_ECONNABORTED, leading to early termination of ongoing tasks.
+ */
+void h2_mplx_abort(h2_mplx *mplx);
+
+void h2_mplx_task_done(h2_mplx *m, int stream_id);
+
+/*******************************************************************************
+ * IO lifetime of streams.
+ ******************************************************************************/
+/**
+ * Prepares the multiplexer to handle in-/output on the given stream id.
+ */
+struct h2_stream *h2_mplx_open_io(h2_mplx *mplx, int stream_id);
+
+/**
+ * Ends cleanup of a stream in sync with execution thread.
+ */
+apr_status_t h2_mplx_cleanup_stream(h2_mplx *m, struct h2_stream *stream);
+
+/* Return != 0 iff the multiplexer has data for the given stream. 
+ */
+int h2_mplx_out_has_data_for(h2_mplx *m, int stream_id);
+
+/**
+ * Waits on output data from any stream in this session to become available. 
+ * Returns APR_TIMEUP if no data arrived in the given time.
+ */
+apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout,
+                                 struct apr_thread_cond_t *iowait);
+
+/*******************************************************************************
+ * Stream processing.
+ ******************************************************************************/
+
+/**
+ * Perform the task on the given stream.
+ */
+apr_status_t h2_mplx_do_task(h2_mplx *mplx, struct h2_task *task);
+
+struct h2_task *h2_mplx_pop_task(h2_mplx *mplx, int *has_more);
+
+/*******************************************************************************
+ * Input handling of streams.
+ ******************************************************************************/
+
+/**
+ * Reads a buckets for the given stream_id. Will return ARP_EAGAIN when
+ * called with APR_NONBLOCK_READ and no data present. Will return APR_EOF
+ * when the end of the stream input has been reached.
+ * The condition passed in will be used for blocking/signalling and will
+ * be protected by the mplx's own mutex.
+ */
+apr_status_t h2_mplx_in_read(h2_mplx *m, apr_read_type_e block,
+                             int stream_id, apr_bucket_brigade *bb,
+                             struct apr_thread_cond_t *iowait);
+
+/**
+ * Appends data to the input of the given stream. Storage of input data is
+ * not subject to flow control.
+ */
+apr_status_t h2_mplx_in_write(h2_mplx *mplx, int stream_id, 
+                              apr_bucket_brigade *bb);
+
+/**
+ * Closes the input for the given stream_id.
+ */
+apr_status_t h2_mplx_in_close(h2_mplx *m, int stream_id);
+
+/**
+ * Returns != 0 iff the input for the given stream has been closed. There
+ * could still be data queued, but it can be read without blocking.
+ */
+int h2_mplx_in_has_eos_for(h2_mplx *m, int stream_id);
+
+/**
+ * Callback invoked for every stream that had input data read since
+ * the last invocation.
+ */
+typedef void h2_mplx_consumed_cb(void *ctx, int stream_id, apr_size_t consumed);
+
+/**
+ * Invoke the callback for all streams that had bytes read since the last
+ * call to this function. If no stream had input data consumed, the callback
+ * is not invoked.
+ * Returns APR_SUCCESS when an update happened, APR_EAGAIN if no update
+ * happened.
+ */
+apr_status_t h2_mplx_in_update_windows(h2_mplx *m, 
+                                       h2_mplx_consumed_cb *cb, void *ctx);
+
+/*******************************************************************************
+ * Output handling of streams.
+ ******************************************************************************/
+
+/**
+ * Get a stream whose response is ready for submit. Will set response and
+ * any out data available in stream. 
+ * @param m the mplxer to get a response from
+ * @param bb the brigade to place any existing repsonse body data into
+ */
+struct h2_stream *h2_mplx_next_submit(h2_mplx *m, 
+                                      struct h2_stream_set *streams);
+
+/**
+ * Reads output data from the given stream. Will never block, but
+ * return APR_EAGAIN until data arrives or the stream is closed.
+ */
+apr_status_t h2_mplx_out_read(h2_mplx *mplx, int stream_id, 
+                              char *buffer, apr_size_t *plen, int *peos);
+
+apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, 
+                               h2_io_data_cb *cb, void *ctx, 
+                               apr_size_t *plen, int *peos);
+
+/**
+ * Opens the output for the given stream with the specified response.
+ */
+apr_status_t h2_mplx_out_open(h2_mplx *mplx, int stream_id,
+                              struct h2_response *response,
+                              ap_filter_t* filter, apr_bucket_brigade *bb,
+                              struct apr_thread_cond_t *iowait);
+
+/**
+ * Append the brigade to the stream output. Might block if amount
+ * of bytes buffered reaches configured max.
+ * @param stream_id the stream identifier
+ * @param filter the apache filter context of the data
+ * @param bb the bucket brigade to append
+ * @param iowait a conditional used for block/signalling in h2_mplx
+ */
+apr_status_t h2_mplx_out_write(h2_mplx *mplx, int stream_id, 
+                               ap_filter_t* filter, apr_bucket_brigade *bb,
+                               struct apr_thread_cond_t *iowait);
+
+/**
+ * Closes the output stream. Readers of this stream will get all pending 
+ * data and then only APR_EOF as result. 
+ */
+apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id);
+
+/*******************************************************************************
+ * h2_mplx list Manipulation.
+ ******************************************************************************/
+
+/**
+ * The magic pointer value that indicates the head of a h2_mplx list
+ * @param  b The mplx list
+ * @return The magic pointer value
+ */
+#define H2_MPLX_LIST_SENTINEL(b)       APR_RING_SENTINEL((b), h2_mplx, link)
+
+/**
+ * Determine if the mplx list is empty
+ * @param b The list to check
+ * @return true or false
+ */
+#define H2_MPLX_LIST_EMPTY(b)  APR_RING_EMPTY((b), h2_mplx, link)
+
+/**
+ * Return the first mplx in a list
+ * @param b The list to query
+ * @return The first mplx in the list
+ */
+#define H2_MPLX_LIST_FIRST(b)  APR_RING_FIRST(b)
+
+/**
+ * Return the last mplx in a list
+ * @param b The list to query
+ * @return The last mplx int he list
+ */
+#define H2_MPLX_LIST_LAST(b)   APR_RING_LAST(b)
+
+/**
+ * Insert a single mplx at the front of a list
+ * @param b The list to add to
+ * @param e The mplx to insert
+ */
+#define H2_MPLX_LIST_INSERT_HEAD(b, e) do {                            \
+h2_mplx *ap__b = (e);                                        \
+APR_RING_INSERT_HEAD((b), ap__b, h2_mplx, link);       \
+} while (0)
+
+/**
+ * Insert a single mplx at the end of a list
+ * @param b The list to add to
+ * @param e The mplx to insert
+ */
+#define H2_MPLX_LIST_INSERT_TAIL(b, e) do {                            \
+h2_mplx *ap__b = (e);                                  \
+APR_RING_INSERT_TAIL((b), ap__b, h2_mplx, link);       \
+} while (0)
+
+/**
+ * Get the next mplx in the list
+ * @param e The current mplx
+ * @return The next mplx
+ */
+#define H2_MPLX_NEXT(e)        APR_RING_NEXT((e), link)
+/**
+ * Get the previous mplx in the list
+ * @param e The current mplx
+ * @return The previous mplx
+ */
+#define H2_MPLX_PREV(e)        APR_RING_PREV((e), link)
+
+/**
+ * Remove a mplx from its list
+ * @param e The mplx to remove
+ */
+#define H2_MPLX_REMOVE(e)      APR_RING_REMOVE((e), link)
+
+
+#endif /* defined(__mod_h2__h2_mplx__) */
diff --git a/modules/http2/mod_h2/h2_private.h b/modules/http2/mod_h2/h2_private.h
new file mode 100644 (file)
index 0000000..6931278
--- /dev/null
@@ -0,0 +1,36 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mod_h2_h2_private_h
+#define mod_h2_h2_private_h
+
+#include <nghttp2/nghttp2.h>
+
+extern module AP_MODULE_DECLARE_DATA h2_module;
+
+APLOG_USE_MODULE(h2);
+
+
+#define H2_HEADER_METHOD     ":method"
+#define H2_HEADER_METHOD_LEN 7
+#define H2_HEADER_SCHEME     ":scheme"
+#define H2_HEADER_SCHEME_LEN 7
+#define H2_HEADER_AUTH       ":authority"
+#define H2_HEADER_AUTH_LEN   10
+#define H2_HEADER_PATH       ":path"
+#define H2_HEADER_PATH_LEN   5
+#define H2_CRLF             "\r\n"
+
+#endif
diff --git a/modules/http2/mod_h2/h2_request.c b/modules/http2/mod_h2/h2_request.c
new file mode 100644 (file)
index 0000000..62744c0
--- /dev/null
@@ -0,0 +1,176 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_mplx.h"
+#include "h2_to_h1.h"
+#include "h2_request.h"
+#include "h2_task.h"
+#include "h2_util.h"
+
+
+h2_request *h2_request_create(int id, apr_pool_t *pool, 
+                              apr_bucket_alloc_t *bucket_alloc)
+{
+    h2_request *req = apr_pcalloc(pool, sizeof(h2_request));
+    if (req) {
+        req->id = id;
+        req->pool = pool;
+        req->bucket_alloc = bucket_alloc;
+    }
+    return req;
+}
+
+void h2_request_destroy(h2_request *req)
+{
+    if (req->to_h1) {
+        h2_to_h1_destroy(req->to_h1);
+        req->to_h1 = NULL;
+    }
+}
+
+static apr_status_t insert_request_line(h2_request *req, h2_mplx *m);
+
+apr_status_t h2_request_rwrite(h2_request *req, request_rec *r, h2_mplx *m)
+{
+    req->method = r->method;
+    req->path = r->uri;
+    req->authority = r->hostname;
+    if (!strchr(req->authority, ':') && r->parsed_uri.port_str) {
+        req->authority = apr_psprintf(req->pool, "%s:%s", req->authority,
+                                      r->parsed_uri.port_str);
+    }
+    req->scheme = NULL;
+    
+    
+    apr_status_t status = insert_request_line(req, m);
+    if (status == APR_SUCCESS) {
+        status = h2_to_h1_add_headers(req->to_h1, r->headers_in);
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
+                  "h2_request(%d): written request %s %s, host=%s",
+                  req->id, req->method, req->path, req->authority);
+    
+    return status;
+}
+
+apr_status_t h2_request_write_header(h2_request *req,
+                                     const char *name, size_t nlen,
+                                     const char *value, size_t vlen,
+                                     h2_mplx *m)
+{
+    apr_status_t status = APR_SUCCESS;
+    
+    if (nlen <= 0) {
+        return status;
+    }
+    
+    if (name[0] == ':') {
+        /* pseudo header, see ch. 8.1.2.3, always should come first */
+        if (req->to_h1) {
+            ap_log_perror(APLOG_MARK, APLOG_ERR, 0, req->pool,
+                          "h2_request(%d): pseudo header after request start",
+                          req->id);
+            return APR_EGENERAL;
+        }
+        
+        if (H2_HEADER_METHOD_LEN == nlen
+            && !strncmp(H2_HEADER_METHOD, name, nlen)) {
+            req->method = apr_pstrndup(req->pool, value, vlen);
+        }
+        else if (H2_HEADER_SCHEME_LEN == nlen
+                 && !strncmp(H2_HEADER_SCHEME, name, nlen)) {
+            req->scheme = apr_pstrndup(req->pool, value, vlen);
+        }
+        else if (H2_HEADER_PATH_LEN == nlen
+                 && !strncmp(H2_HEADER_PATH, name, nlen)) {
+            req->path = apr_pstrndup(req->pool, value, vlen);
+        }
+        else if (H2_HEADER_AUTH_LEN == nlen
+                 && !strncmp(H2_HEADER_AUTH, name, nlen)) {
+            req->authority = apr_pstrndup(req->pool, value, vlen);
+        }
+        else {
+            char buffer[32];
+            memset(buffer, 0, 32);
+            strncpy(buffer, name, (nlen > 31)? 31 : nlen);
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, req->pool,
+                          "h2_request(%d): ignoring unknown pseudo header %s",
+                          req->id, buffer);
+        }
+    }
+    else {
+        /* non-pseudo header, append to work bucket of stream */
+        if (!req->to_h1) {
+            status = insert_request_line(req, m);
+            if (status != APR_SUCCESS) {
+                return status;
+            }
+        }
+        
+        if (status == APR_SUCCESS) {
+            status = h2_to_h1_add_header(req->to_h1,
+                                         name, nlen, value, vlen);
+        }
+    }
+    
+    return status;
+}
+
+apr_status_t h2_request_write_data(h2_request *req,
+                                   const char *data, size_t len)
+{
+    return h2_to_h1_add_data(req->to_h1, data, len);
+}
+
+apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m,
+                                    h2_task *task, int eos)
+{
+    if (!req->to_h1) {
+        apr_status_t status = insert_request_line(req, m);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+    }
+    return h2_to_h1_end_headers(req->to_h1, task, eos);
+}
+
+apr_status_t h2_request_close(h2_request *req)
+{
+    return h2_to_h1_close(req->to_h1);
+}
+
+static apr_status_t insert_request_line(h2_request *req, h2_mplx *m)
+{
+    req->to_h1 = h2_to_h1_create(req->id, req->pool, req->bucket_alloc, 
+                                 req->method, req->path, req->authority, m);
+    return req->to_h1? APR_SUCCESS : APR_ENOMEM;
+}
+
+apr_status_t h2_request_flush(h2_request *req)
+{
+    return h2_to_h1_flush(req->to_h1);
+}
+
diff --git a/modules/http2/mod_h2/h2_request.h b/modules/http2/mod_h2/h2_request.h
new file mode 100644 (file)
index 0000000..dac8c6f
--- /dev/null
@@ -0,0 +1,67 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_request__
+#define __mod_h2__h2_request__
+
+/* h2_request is the transformer of HTTP2 streams into HTTP/1.1 internal
+ * format that will be fed to various httpd input filters to finally
+ * become a request_rec to be handled by soemone.
+ *
+ * Ideally, we would make a request_rec without serializing the headers
+ * we have only to make someone else parse them back.
+ */
+struct h2_to_h1;
+struct h2_mplx;
+struct h2_task;
+
+typedef struct h2_request h2_request;
+
+struct h2_request {
+    int id;                 /* http2 stream id */
+    apr_pool_t *pool;
+    apr_bucket_alloc_t *bucket_alloc;
+    struct h2_to_h1 *to_h1; /* Converter to HTTP/1.1 format*/
+    
+    /* pseudo header values, see ch. 8.1.2.3 */
+    const char *method;
+    const char *path;
+    const char *authority;
+    const char *scheme;
+};
+
+h2_request *h2_request_create(int id, apr_pool_t *pool, 
+                              apr_bucket_alloc_t *bucket_alloc);
+void h2_request_destroy(h2_request *req);
+
+apr_status_t h2_request_flush(h2_request *req);
+
+apr_status_t h2_request_write_header(h2_request *req,
+                                     const char *name, size_t nlen,
+                                     const char *value, size_t vlen,
+                                     struct h2_mplx *m);
+
+apr_status_t h2_request_write_data(h2_request *request,
+                                   const char *data, size_t len);
+
+apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m, 
+                                    struct h2_task *task, int eos);
+
+apr_status_t h2_request_close(h2_request *req);
+
+apr_status_t h2_request_rwrite(h2_request *req, request_rec *r,
+                               struct h2_mplx *m);
+
+#endif /* defined(__mod_h2__h2_request__) */
diff --git a/modules/http2/mod_h2/h2_response.c b/modules/http2/mod_h2/h2_response.c
new file mode 100644 (file)
index 0000000..837b03f
--- /dev/null
@@ -0,0 +1,235 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include <nghttp2/nghttp2.h>
+
+#include "h2_private.h"
+#include "h2_util.h"
+#include "h2_response.h"
+
+static void convert_header(h2_response *response, apr_table_t *headers,
+                           const char *http_status, request_rec *r);
+static int ignore_header(const char *name) 
+{
+    return (H2_HD_MATCH_LIT_CS("connection", name)
+            || H2_HD_MATCH_LIT_CS("proxy-connection", name)
+            || H2_HD_MATCH_LIT_CS("upgrade", name)
+            || H2_HD_MATCH_LIT_CS("keep-alive", name)
+            || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
+}
+
+h2_response *h2_response_create(int stream_id,
+                                const char *http_status,
+                                apr_array_header_t *hlines,
+                                apr_pool_t *pool)
+{
+    apr_table_t *header;
+    h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
+    if (response == NULL) {
+        return NULL;
+    }
+    
+    response->stream_id = stream_id;
+    response->content_length = -1;
+    
+    if (hlines) {
+        header = apr_table_make(pool, hlines->nelts);        
+        for (int i = 0; i < hlines->nelts; ++i) {
+            char *hline = ((char **)hlines->elts)[i];
+            char *sep = strchr(hline, ':');
+            if (!sep) {
+                ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
+                              "h2_response(%d): invalid header[%d] '%s'",
+                              response->stream_id, i, (char*)hline);
+                /* not valid format, abort */
+                return NULL;
+            }
+            (*sep++) = '\0';
+            while (*sep == ' ' || *sep == '\t') {
+                ++sep;
+            }
+            if (ignore_header(hline)) {
+                /* never forward, ch. 8.1.2.2 */
+            }
+            else {
+                apr_table_merge(header, hline, sep);
+                if (*sep && H2_HD_MATCH_LIT_CS("content-length", hline)) {
+                    char *end;
+                    response->content_length = apr_strtoi64(sep, &end, 10);
+                    if (sep == end) {
+                        ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, 
+                                      pool, "h2_response(%d): content-length"
+                                      " value not parsed: %s", 
+                                      response->stream_id, sep);
+                        response->content_length = -1;
+                    }
+                }
+            }
+        }
+    }
+    else {
+        header = apr_table_make(pool, 0);        
+    }
+    
+    convert_header(response, header, http_status, NULL);
+    return response->headers? response : NULL;
+}
+
+h2_response *h2_response_rcreate(int stream_id, request_rec *r,
+                                 apr_table_t *header, apr_pool_t *pool)
+{
+    h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
+    if (response == NULL) {
+        return NULL;
+    }
+    
+    response->stream_id = stream_id;
+    response->content_length = -1;
+    convert_header(response, header, apr_psprintf(pool, "%d", r->status), r);
+    
+    return response->headers? response : NULL;
+}
+
+void h2_response_cleanup(h2_response *response)
+{
+    if (response->headers) {
+        if (--response->headers->refs == 0) {
+            free(response->headers);
+        }
+        response->headers = NULL;
+    }
+}
+
+void h2_response_destroy(h2_response *response)
+{
+    h2_response_cleanup(response);
+}
+
+void h2_response_copy(h2_response *to, h2_response *from)
+{
+    h2_response_cleanup(to);
+    *to = *from;
+    if (from->headers) {
+        ++from->headers->refs;
+    }
+}
+
+typedef struct {
+    nghttp2_nv *nv;
+    size_t nvlen;
+    size_t nvstrlen;
+    size_t offset;
+    char *strbuf;
+    h2_response *response;
+    int debug;
+    request_rec *r;
+} nvctx_t;
+
+static int count_headers(void *ctx, const char *key, const char *value)
+{
+    if (!ignore_header(key)) {
+        nvctx_t *nvctx = (nvctx_t*)ctx;
+        nvctx->nvlen++;
+        nvctx->nvstrlen += strlen(key) + strlen(value) + 2;
+    }
+    return 1;
+}
+
+#define NV_ADD_LIT_CS(nv, k, v)     addnv_lit_cs(nv, k, sizeof(k) - 1, v, strlen(v))
+#define NV_ADD_CS_CS(nv, k, v)      addnv_cs_cs(nv, k, strlen(k), v, strlen(v))
+#define NV_BUF_ADD(nv, s, len)      memcpy(nv->strbuf, s, len); \
+s = nv->strbuf; \
+nv->strbuf += len + 1
+
+static void addnv_cs_cs(nvctx_t *ctx, const char *key, size_t key_len,
+                        const char *value, size_t val_len)
+{
+    nghttp2_nv *nv = &ctx->nv[ctx->offset];
+    
+    NV_BUF_ADD(ctx, key, key_len);
+    NV_BUF_ADD(ctx, value, val_len);
+    
+    nv->name = (uint8_t*)key;
+    nv->namelen = key_len;
+    nv->value = (uint8_t*)value;
+    nv->valuelen = val_len;
+    
+    ctx->offset++;
+}
+
+static void addnv_lit_cs(nvctx_t *ctx, const char *key, size_t key_len,
+                         const char *value, size_t val_len)
+{
+    nghttp2_nv *nv = &ctx->nv[ctx->offset];
+    
+    NV_BUF_ADD(ctx, value, val_len);
+    
+    nv->name = (uint8_t*)key;
+    nv->namelen = key_len;
+    nv->value = (uint8_t*)value;
+    nv->valuelen = val_len;
+    
+    ctx->offset++;
+}
+
+static int add_header(void *ctx, const char *key, const char *value)
+{
+    if (!ignore_header(key)) {
+        nvctx_t *nvctx = (nvctx_t*)ctx;
+        if (nvctx->debug) {
+            ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, 
+                          nvctx->r, "h2_response(%d) header -> %s: %s",
+                          nvctx->response->stream_id, key, value);
+        }
+        NV_ADD_CS_CS(ctx, key, value);
+    }
+    return 1;
+}
+
+static void convert_header(h2_response *response, apr_table_t *headers,
+                           const char *status, request_rec *r)
+{
+    nvctx_t ctx = { NULL, 1, strlen(status) + 1, 0, NULL, 
+        response, r? APLOGrdebug(r) : 0, r };
+    
+    apr_table_do(count_headers, &ctx, headers, NULL);
+    
+    size_t n =  (sizeof(h2_headers)
+                 + (ctx.nvlen * sizeof(nghttp2_nv)) + ctx.nvstrlen); 
+    h2_headers *h = calloc(1, n);
+    if (h) {
+        ctx.nv = (nghttp2_nv*)(h + 1);
+        ctx.strbuf = (char*)&ctx.nv[ctx.nvlen];
+        
+        NV_ADD_LIT_CS(&ctx, ":status", status);
+        apr_table_do(add_header, &ctx, headers, NULL);
+        
+        h->nv = ctx.nv;
+        h->nvlen = ctx.nvlen;
+        h->status = (const char *)ctx.nv[0].value;
+        h->refs = 1;
+        response->headers = h;
+    }
+}
+
diff --git a/modules/http2/mod_h2/h2_response.h b/modules/http2/mod_h2/h2_response.h
new file mode 100644 (file)
index 0000000..d6174f5
--- /dev/null
@@ -0,0 +1,48 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_response__
+#define __mod_h2__h2_response__
+
+/* h2_response is just the data belonging the the head of a HTTP response,
+ * suitable prepared to be fed to nghttp2 for response submit. 
+ */
+typedef struct h2_headers {
+    nghttp2_nv *nv;
+    apr_size_t nvlen;
+    const char *status;
+    volatile int refs;
+} h2_headers;
+
+typedef struct h2_response {
+    int stream_id;
+    apr_off_t content_length;
+    h2_headers *headers;
+} h2_response;
+
+h2_response *h2_response_create(int stream_id,
+                                  const char *http_status,
+                                  apr_array_header_t *hlines,
+                                  apr_pool_t *pool);
+
+h2_response *h2_response_rcreate(int stream_id, request_rec *r,
+                                 apr_table_t *header, apr_pool_t *pool);
+
+void h2_response_cleanup(h2_response *response);
+void h2_response_destroy(h2_response *response);
+
+void h2_response_copy(h2_response *to, h2_response *from);
+
+#endif /* defined(__mod_h2__h2_response__) */
diff --git a/modules/http2/mod_h2/h2_session.c b/modules/http2/mod_h2/h2_session.c
new file mode 100644 (file)
index 0000000..06f8503
--- /dev/null
@@ -0,0 +1,1225 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <apr_thread_cond.h>
+#include <apr_base64.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_config.h"
+#include "h2_h2.h"
+#include "h2_mplx.h"
+#include "h2_response.h"
+#include "h2_stream.h"
+#include "h2_stream_set.h"
+#include "h2_from_h1.h"
+#include "h2_task.h"
+#include "h2_session.h"
+#include "h2_util.h"
+#include "h2_version.h"
+#include "h2_workers.h"
+
+static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
+
+static int h2_session_status_from_apr_status(apr_status_t rv)
+{
+    switch (rv) {
+        case APR_SUCCESS:
+            return NGHTTP2_NO_ERROR;
+        case APR_EAGAIN:
+        case APR_TIMEUP:
+            return NGHTTP2_ERR_WOULDBLOCK;
+        case APR_EOF:
+            return NGHTTP2_ERR_EOF;
+        default:
+            return NGHTTP2_ERR_PROTO;
+    }
+}
+
+static int stream_open(h2_session *session, int stream_id)
+{
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    
+    h2_stream * stream = h2_mplx_open_io(session->mplx, stream_id);
+    if (stream) {
+        h2_stream_set_add(session->streams, stream);
+        
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                      "h2_session: stream(%ld-%d): opened",
+                      session->id, stream_id);
+        
+        return 0;
+    }
+    
+    ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, session->c,
+                  "h2_session: stream(%ld-%d): unable to create",
+                  session->id, stream_id);
+    return NGHTTP2_ERR_INVALID_STREAM_ID;
+}
+
+static apr_status_t stream_end_headers(h2_session *session,
+                                       h2_stream *stream, int eos)
+{
+    (void)session;
+    return h2_stream_write_eoh(stream, eos);
+}
+
+static apr_status_t send_data(h2_session *session, const char *data, 
+                              apr_size_t length);
+
+/*
+ * Callback when nghttp2 wants to send bytes back to the client.
+ */
+static ssize_t send_cb(nghttp2_session *ngh2,
+                       const uint8_t *data, size_t length,
+                       int flags, void *userp)
+{
+    h2_session *session = (h2_session *)userp;
+    apr_status_t status = send_data(session, (const char *)data, length);
+    (void)ngh2; (void)flags;
+    
+    if (status == APR_SUCCESS) {
+        return length;
+    }
+    if (status == APR_EAGAIN || status == APR_TIMEUP) {
+        return NGHTTP2_ERR_WOULDBLOCK;
+    }
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
+                  "h2_session: send error");
+    return h2_session_status_from_apr_status(status);
+}
+
+static int on_invalid_frame_recv_cb(nghttp2_session *ngh2,
+                                    const nghttp2_frame *frame,
+                                    nghttp2_error error, void *userp)
+{
+    h2_session *session = (h2_session *)userp;
+    (void)ngh2;
+    
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    if (APLOGctrace2(session->c)) {
+        char buffer[256];
+        
+        frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                      "h2_session: callback on_invalid_frame_recv error=%d %s",
+                      (int)error, buffer);
+    }
+    return 0;
+}
+
+static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
+                                 int32_t stream_id,
+                                 const uint8_t *data, size_t len, void *userp)
+{
+    int rv;
+    h2_session *session = (h2_session *)userp;
+    (void)flags;
+    
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    h2_stream * stream = h2_stream_set_get(session->streams, stream_id);
+    if (!stream) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
+                      "h2_session:  stream(%ld-%d): on_data_chunk for unknown stream",
+                      session->id, (int)stream_id);
+        rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
+                                       NGHTTP2_INTERNAL_ERROR);
+        if (nghttp2_is_fatal(rv)) {
+            return NGHTTP2_ERR_CALLBACK_FAILURE;
+        }
+        return 0;
+    }
+    
+    apr_status_t status = h2_stream_write_data(stream, (const char *)data, len);
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
+                  "h2_stream(%ld-%d): written DATA, length %d",
+                  session->id, stream_id, (int)len);
+    if (status != APR_SUCCESS) {
+        rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
+                                       NGHTTP2_INTERNAL_ERROR);
+        if (nghttp2_is_fatal(rv)) {
+            return NGHTTP2_ERR_CALLBACK_FAILURE;
+        }
+    }
+    return 0;
+}
+
+static int before_frame_send_cb(nghttp2_session *ngh2,
+                                const nghttp2_frame *frame,
+                                void *userp)
+{
+    h2_session *session = (h2_session *)userp;
+    (void)ngh2;
+
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    if (APLOGctrace2(session->c)) {
+        char buffer[256];
+        frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                      "h2_session(%ld): before_frame_send %s", 
+                      session->id, buffer);
+    }
+    return 0;
+}
+
+static int on_frame_send_cb(nghttp2_session *ngh2,
+                            const nghttp2_frame *frame,
+                            void *userp)
+{
+    h2_session *session = (h2_session *)userp;
+    (void)ngh2; (void)frame;
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                  "h2_session(%ld): on_frame_send", session->id);
+    return 0;
+}
+
+static int on_frame_not_send_cb(nghttp2_session *ngh2,
+                                const nghttp2_frame *frame,
+                                int lib_error_code, void *userp)
+{
+    h2_session *session = (h2_session *)userp;
+    (void)ngh2;
+    
+    if (APLOGctrace2(session->c)) {
+        char buffer[256];
+        
+        frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                      "h2_session: callback on_frame_not_send error=%d %s",
+                      lib_error_code, buffer);
+    }
+    return 0;
+}
+
+static apr_status_t stream_destroy(h2_session *session, h2_stream *stream) 
+{
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                  "h2_stream(%ld-%d): closing", session->id, (int)stream->id);
+    h2_stream_set_remove(session->streams, stream);
+    return h2_mplx_cleanup_stream(session->mplx, stream);
+}
+
+static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
+                              uint32_t error_code, void *userp)
+{
+    h2_session *session = (h2_session *)userp;
+    (void)ngh2;
+    
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    h2_stream *stream = h2_stream_set_get(session->streams, stream_id);
+    if (stream) {
+        stream_destroy(session, stream);
+    }
+    
+    if (error_code) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                      "h2_stream(%ld-%d): close error %d",
+                      session->id, (int)stream_id, error_code);
+    }
+    
+    return 0;
+}
+
+static int on_begin_headers_cb(nghttp2_session *ngh2,
+                               const nghttp2_frame *frame, void *userp)
+{
+    (void)ngh2;
+    /* This starts a new stream. */
+    int rv = stream_open((h2_session *)userp, frame->hd.stream_id);
+    if (rv != NGHTTP2_ERR_CALLBACK_FAILURE) {
+      /* on_header_cb or on_frame_recv_cb will dectect that stream
+         does not exist and submit RST_STREAM. */
+      return 0;
+    }
+    return NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
+static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
+                        const uint8_t *name, size_t namelen,
+                        const uint8_t *value, size_t valuelen,
+                        uint8_t flags,
+                        void *userp)
+{
+    h2_session *session = (h2_session *)userp;
+    (void)ngh2; (void)flags;
+    
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    h2_stream * stream = h2_stream_set_get(session->streams,
+                                           frame->hd.stream_id);
+    if (!stream) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
+                      "h2_session:  stream(%ld-%d): on_header for unknown stream",
+                      session->id, (int)frame->hd.stream_id);
+        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+    }
+    
+    apr_status_t status = h2_stream_write_header(stream,
+                                               (const char *)name, namelen,
+                                               (const char *)value, valuelen);
+    if (status != APR_SUCCESS) {
+        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+    }
+    return 0;
+}
+
+/**
+ * nghttp2 session has received a complete frame. Most, it uses
+ * for processing of internal state. HEADER and DATA frames however
+ * we need to handle ourself.
+ */
+static int on_frame_recv_cb(nghttp2_session *ng2s,
+                            const nghttp2_frame *frame,
+                            void *userp)
+{
+    int rv;
+    h2_session *session = (h2_session *)userp;
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    apr_status_t status = APR_SUCCESS;
+    
+    ++session->frames_received;
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
+                  "h2_session(%ld): on_frame_rcv #%ld, type=%d", session->id,
+                  (long)session->frames_received, frame->hd.type);
+    switch (frame->hd.type) {
+        case NGHTTP2_HEADERS: {
+            h2_stream * stream = h2_stream_set_get(session->streams,
+                                                   frame->hd.stream_id);
+            if (stream == NULL) {
+                ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
+                              "h2_session:  stream(%ld-%d): HEADERS frame "
+                              "for unknown stream", session->id,
+                              (int)frame->hd.stream_id);
+                rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
+                                               frame->hd.stream_id,
+                                               NGHTTP2_INTERNAL_ERROR);
+                if (nghttp2_is_fatal(rv)) {
+                    return NGHTTP2_ERR_CALLBACK_FAILURE;
+                }
+                return 0;
+            }
+
+            int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM);
+            status = stream_end_headers(session, stream, eos);
+
+            break;
+        }
+        case NGHTTP2_DATA: {
+            h2_stream * stream = h2_stream_set_get(session->streams,
+                                                   frame->hd.stream_id);
+            if (stream == NULL) {
+                ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
+                              "h2_session:  stream(%ld-%d): DATA frame "
+                              "for unknown stream", session->id,
+                              (int)frame->hd.stream_id);
+                rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
+                                               frame->hd.stream_id,
+                                               NGHTTP2_INTERNAL_ERROR);
+                if (nghttp2_is_fatal(rv)) {
+                    return NGHTTP2_ERR_CALLBACK_FAILURE;
+                }
+                return 0;
+            }
+            break;
+        }
+        case NGHTTP2_PRIORITY: {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
+                          "h2_session:  stream(%ld-%d): PRIORITY frame "
+                          " weight=%d, dependsOn=%d, exclusive=%d", 
+                          session->id, (int)frame->hd.stream_id,
+                          frame->priority.pri_spec.weight,
+                          frame->priority.pri_spec.stream_id,
+                          frame->priority.pri_spec.exclusive);
+            break;
+        }
+        default:
+            if (APLOGctrace2(session->c)) {
+                char buffer[256];
+                
+                frame_print(frame, buffer,
+                            sizeof(buffer)/sizeof(buffer[0]));
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                              "h2_session: on_frame_rcv %s", buffer);
+            }
+            break;
+    }
+
+    /* only DATA and HEADERS frame can bear END_STREAM flag.  Other
+       frame types may have other flag which has the same value, so we
+       have to check the frame type first.  */
+    if ((frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) &&
+        frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+        h2_stream * stream = h2_stream_set_get(session->streams,
+                                               frame->hd.stream_id);
+        if (stream != NULL) {
+            status = h2_stream_write_eos(stream);
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
+                          "h2_stream(%ld-%d): input closed",
+                          session->id, (int)frame->hd.stream_id);
+        }
+    }
+    
+    if (status != APR_SUCCESS) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
+                      "h2_session: stream(%ld-%d): error handling frame",
+                      session->id, (int)frame->hd.stream_id);
+        rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
+                                       frame->hd.stream_id,
+                                       NGHTTP2_INTERNAL_ERROR);
+        if (nghttp2_is_fatal(rv)) {
+            return NGHTTP2_ERR_CALLBACK_FAILURE;
+        }
+        return 0;
+    }
+    
+    return 0;
+}
+
+static apr_status_t send_data(h2_session *session, const char *data, 
+                              apr_size_t length)
+{
+    return h2_conn_io_write(&session->io, data, length);
+}
+
+#if NGHTTP2_HAS_DATA_CB
+
+static apr_status_t pass_data(void *ctx, 
+                              const char *data, apr_size_t length)
+{
+    return send_data((h2_session*)ctx, data, length);
+}
+
+int on_send_data_cb(nghttp2_session *ngh2, 
+                    nghttp2_frame *frame, 
+                    const uint8_t *framehd, 
+                    size_t length, 
+                    nghttp2_data_source *source, 
+                    void *userp)
+{
+    apr_status_t status = APR_SUCCESS;
+    h2_session *session = (h2_session *)userp;
+    int stream_id = (int)frame->hd.stream_id;
+    const unsigned char padlen = frame->data.padlen;
+    apr_size_t frame_len = 9 + (padlen? 1 : 0) + length + padlen;
+    apr_size_t data_len = length;
+    apr_size_t avail, chunk_len;
+    int rv = 0;
+    int eos;
+    h2_stream *stream;
+    
+    if (session->aborted) {
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    
+    stream = h2_stream_set_get(session->streams, stream_id);
+    if (!stream) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
+                      "h2_stream(%ld-%d): send_data",
+                      session->id, (int)stream_id);
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    
+    status = send_data(session, (const char *)framehd, 9);
+    if (status == APR_SUCCESS) {
+        if (padlen) {
+            status = send_data(session, (const char *)&padlen, 1);
+        }
+
+        if (status == APR_SUCCESS) {
+            apr_size_t len = length;
+            int eos = 0;
+            status = h2_stream_readx(stream, pass_data, session, 
+                                     &len, &eos);
+            if (status == APR_SUCCESS && len != length) {
+                status = APR_EINVAL;
+            }
+        }
+        
+        if (status == APR_SUCCESS && padlen) {
+            if (padlen) {
+                char pad[256];
+                memset(pad, 0, padlen);
+                status = send_data(session, pad, padlen);
+            }
+        }
+    }
+    
+    if (status == APR_SUCCESS) {
+        return 0;
+    }
+    else if (status != APR_EOF) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
+                      "h2_stream(%ld-%d): failed send_data_cb",
+                      session->id, (int)stream_id);
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    
+    return h2_session_status_from_apr_status(status);
+}
+
+#endif
+
+
+#define NGH2_SET_CALLBACK(callbacks, name, fn)\
+nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
+
+static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb)
+{
+    int rv = nghttp2_session_callbacks_new(pcb);
+    if (rv != 0) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+                      "nghttp2_session_callbacks_new: %s",
+                      nghttp2_strerror(rv));
+        return APR_EGENERAL;
+    }
+    
+    NGH2_SET_CALLBACK(*pcb, send, send_cb);
+    NGH2_SET_CALLBACK(*pcb, on_frame_recv, on_frame_recv_cb);
+    NGH2_SET_CALLBACK(*pcb, on_invalid_frame_recv, on_invalid_frame_recv_cb);
+    NGH2_SET_CALLBACK(*pcb, on_data_chunk_recv, on_data_chunk_recv_cb);
+    NGH2_SET_CALLBACK(*pcb, before_frame_send, before_frame_send_cb);
+    NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb);
+    NGH2_SET_CALLBACK(*pcb, on_frame_not_send, on_frame_not_send_cb);
+    NGH2_SET_CALLBACK(*pcb, on_stream_close, on_stream_close_cb);
+    NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb);
+    NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
+#if NGHTTP2_HAS_DATA_CB
+    NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
+#endif
+    
+    return APR_SUCCESS;
+}
+
+static h2_session *h2_session_create_int(conn_rec *c,
+                                         request_rec *r,
+                                         h2_config *config, 
+                                         h2_workers *workers)
+{
+    nghttp2_session_callbacks *callbacks = NULL;
+    nghttp2_option *options = NULL;
+
+    apr_pool_t *pool = NULL;
+    apr_status_t status = apr_pool_create(&pool, r? r->pool : c->pool);
+    if (status != APR_SUCCESS) {
+        return NULL;
+    }
+
+    h2_session *session = apr_pcalloc(pool, sizeof(h2_session));
+    if (session) {
+        session->id = c->id;
+        session->c = c;
+        session->r = r;
+        
+        session->max_stream_count = h2_config_geti(config, H2_CONF_MAX_STREAMS);
+        session->max_stream_mem = h2_config_geti(config, H2_CONF_STREAM_MAX_MEM);
+
+        session->pool = pool;
+        
+        status = apr_thread_cond_create(&session->iowait, session->pool);
+        if (status != APR_SUCCESS) {
+            return NULL;
+        }
+        
+        session->streams = h2_stream_set_create(session->pool);
+        
+        session->workers = workers;
+        session->mplx = h2_mplx_create(c, session->pool, workers);
+        
+        h2_conn_io_init(&session->io, c);
+        session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
+        
+        status = init_callbacks(c, &callbacks);
+        if (status != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c,
+                          "nghttp2: error in init_callbacks");
+            h2_session_destroy(session);
+            return NULL;
+        }
+        
+        int rv = nghttp2_option_new(&options);
+        if (rv != 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
+                          "nghttp2_option_new: %s", nghttp2_strerror(rv));
+            h2_session_destroy(session);
+            return NULL;
+        }
+
+        nghttp2_option_set_peer_max_concurrent_streams(options, 
+                                                       (uint32_t)session->max_stream_count);
+
+        /* We need to handle window updates ourself, otherwise we
+         * get flooded by nghttp2. */
+        nghttp2_option_set_no_auto_window_update(options, 1);
+        
+        rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
+                                         session, options);
+        nghttp2_session_callbacks_del(callbacks);
+        nghttp2_option_del(options);
+        
+        if (rv != 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
+                          "nghttp2_session_server_new: %s",
+                          nghttp2_strerror(rv));
+            h2_session_destroy(session);
+            return NULL;
+        }
+        
+    }
+    return session;
+}
+
+h2_session *h2_session_create(conn_rec *c, h2_config *config, 
+                              h2_workers *workers)
+{
+    return h2_session_create_int(c, NULL, config, workers);
+}
+
+h2_session *h2_session_rcreate(request_rec *r, h2_config *config, 
+                               h2_workers *workers)
+{
+    return h2_session_create_int(r->connection, r, config, workers);
+}
+
+void h2_session_destroy(h2_session *session)
+{
+    AP_DEBUG_ASSERT(session);
+    if (session->mplx) {
+        h2_mplx_release_and_join(session->mplx, session->iowait);
+        session->mplx = NULL;
+    }
+    if (session->streams) {
+        if (h2_stream_set_size(session->streams)) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                          "h2_session(%ld): destroy, %d streams open",
+                          session->id, (int)h2_stream_set_size(session->streams));
+        }
+        h2_stream_set_destroy(session->streams);
+        session->streams = NULL;
+    }
+    if (session->ngh2) {
+        nghttp2_session_del(session->ngh2);
+        session->ngh2 = NULL;
+    }
+    h2_conn_io_destroy(&session->io);
+    
+    if (session->iowait) {
+        apr_thread_cond_destroy(session->iowait);
+        session->iowait = NULL;
+    }
+    
+    if (session->pool) {
+        apr_pool_destroy(session->pool);
+    }
+}
+
+apr_status_t h2_session_goaway(h2_session *session, apr_status_t reason)
+{
+    AP_DEBUG_ASSERT(session);
+    apr_status_t status = APR_SUCCESS;
+    if (session->aborted) {
+        return APR_EINVAL;
+    }
+    
+    int rv = 0;
+    if (reason == APR_SUCCESS) {
+        rv = nghttp2_submit_shutdown_notice(session->ngh2);
+    }
+    else {
+        int err = 0;
+        int last_id = nghttp2_session_get_last_proc_stream_id(session->ngh2);
+        rv = nghttp2_submit_goaway(session->ngh2, last_id,
+                                   NGHTTP2_FLAG_NONE, err, NULL, 0);
+    }
+    if (rv != 0) {
+        status = APR_EGENERAL;
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
+                      "session(%ld): submit goaway: %s",
+                      session->id, nghttp2_strerror(rv));
+    }
+    return status;
+}
+
+static apr_status_t h2_session_abort_int(h2_session *session, int reason)
+{
+    AP_DEBUG_ASSERT(session);
+    if (!session->aborted) {
+        session->aborted = 1;
+        if (session->ngh2) {
+            if (reason) {
+                ap_log_cerror(APLOG_MARK, (reason == NGHTTP2_ERR_EOF)?
+                              APLOG_DEBUG : APLOG_INFO, 0, session->c,
+                              "session(%ld): aborting session, reason=%d %s",
+                              session->id, reason, nghttp2_strerror(reason));
+            }
+            nghttp2_session_terminate_session(session->ngh2, reason);
+            nghttp2_submit_goaway(session->ngh2, 0, 0, reason, NULL, 0);
+            nghttp2_session_send(session->ngh2);
+            h2_conn_io_flush(&session->io);
+        }
+        h2_mplx_abort(session->mplx);
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv)
+{
+    AP_DEBUG_ASSERT(session);
+    if (rv == 0) {
+        rv = NGHTTP2_ERR_PROTO;
+        switch (reason) {
+            case APR_ENOMEM:
+                rv = NGHTTP2_ERR_NOMEM;
+                break;
+            case APR_EOF:
+                rv = 0;
+                break;
+            case APR_EBADF:
+            case APR_ECONNABORTED:
+                rv = NGHTTP2_ERR_EOF;
+                break;
+            default:
+                break;
+        }
+    }
+    return h2_session_abort_int(session, rv);
+}
+
+apr_status_t h2_session_start(h2_session *session, int *rv)
+{
+    AP_DEBUG_ASSERT(session);
+    /* Start the conversation by submitting our SETTINGS frame */
+    apr_status_t status = APR_SUCCESS;
+    *rv = 0;
+    h2_config *config = h2_config_get(session->c);
+    if (session->r) {
+        /* better for vhost matching */
+        config = h2_config_rget(session->r);
+        
+        /* 'h2c' mode: we should have a 'HTTP2-Settings' header with
+         * base64 encoded client settings. */
+        const char *s = apr_table_get(session->r->headers_in, "HTTP2-Settings");
+        if (!s) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r,
+                          "HTTP2-Settings header missing in request");
+            return APR_EINVAL;
+        }
+        unsigned char *cs = NULL;
+        apr_size_t dlen = h2_util_base64url_decode(&cs, s, session->pool);
+        
+        if (APLOGrdebug(session->r)) {
+            char buffer[128];
+            h2_util_hex_dump(buffer, 128, (char*)cs, dlen);
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, session->r,
+                          "upgrading h2c session with HTTP2-Settings: %s -> %s (%d)",
+                          s, buffer, (int)dlen);
+        }
+        
+        *rv = nghttp2_session_upgrade(session->ngh2, (uint8_t*)cs, dlen, NULL);
+        if (*rv != 0) {
+            status = APR_EINVAL;
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
+                          "nghttp2_session_upgrade: %s", nghttp2_strerror(*rv));
+            return status;
+        }
+        
+        /* Now we need to auto-open stream 1 for the request we got. */
+        *rv = stream_open(session, 1);
+        if (*rv != 0) {
+            status = APR_EGENERAL;
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
+                          "open stream 1: %s", nghttp2_strerror(*rv));
+            return status;
+        }
+        
+        h2_stream * stream = h2_stream_set_get(session->streams, 1);
+        if (stream == NULL) {
+            status = APR_EGENERAL;
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
+                          "lookup of stream 1");
+            return status;
+        }
+        
+        status = h2_stream_rwrite(stream, session->r);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        status = stream_end_headers(session, stream, 1);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+    }
+
+    nghttp2_settings_entry settings[] = {
+        { NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
+            h2_config_geti(config, H2_CONF_MAX_HL_SIZE) },
+        { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
+            h2_config_geti(config, H2_CONF_WIN_SIZE) },
+        {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 
+            (uint32_t)session->max_stream_count }, 
+    };
+    *rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE,
+                                 settings,
+                                 sizeof(settings)/sizeof(settings[0]));
+    if (*rv != 0) {
+        status = APR_EGENERAL;
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
+                      "nghttp2_submit_settings: %s", nghttp2_strerror(*rv));
+    }
+    
+    return status;
+}
+
+static int h2_session_want_write(h2_session *session)
+{
+    return nghttp2_session_want_write(session->ngh2);
+}
+
+typedef struct {
+    h2_session *session;
+    int resume_count;
+} resume_ctx;
+
+static int resume_on_data(void *ctx, h2_stream *stream) {
+    resume_ctx *rctx = (resume_ctx*)ctx;
+    h2_session *session = rctx->session;
+    AP_DEBUG_ASSERT(session);
+    AP_DEBUG_ASSERT(stream);
+    
+    if (h2_stream_is_suspended(stream)) {
+        if (h2_mplx_out_has_data_for(stream->m, stream->id)) {
+            h2_stream_set_suspended(stream, 0);
+            ++rctx->resume_count;
+            
+            int rv = nghttp2_session_resume_data(session->ngh2, stream->id);
+            ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
+                          APLOG_ERR : APLOG_DEBUG, 0, session->c,
+                          "h2_stream(%ld-%d): resuming stream %s",
+                          session->id, stream->id, nghttp2_strerror(rv));
+        }
+    }
+    return 1;
+}
+
+static int h2_session_resume_streams_with_data(h2_session *session) {
+    AP_DEBUG_ASSERT(session);
+    if (!h2_stream_set_is_empty(session->streams)
+        && session->mplx && !session->aborted) {
+        resume_ctx ctx = { session, 0 };
+        /* Resume all streams where we have data in the out queue and
+         * which had been suspended before. */
+        h2_stream_set_iter(session->streams, resume_on_data, &ctx);
+        return ctx.resume_count;
+    }
+    return 0;
+}
+
+static void update_window(void *ctx, int stream_id, apr_size_t bytes_read)
+{
+    h2_session *session = (h2_session*)ctx;
+    nghttp2_session_consume(session->ngh2, stream_id, bytes_read);
+}
+
+static apr_status_t h2_session_update_windows(h2_session *session)
+{
+    return h2_mplx_in_update_windows(session->mplx, update_window, session);
+}
+
+apr_status_t h2_session_write(h2_session *session, apr_interval_time_t timeout)
+{
+    apr_status_t status = APR_EAGAIN;
+    h2_stream *stream = NULL;
+    int flush_output = 0;
+    
+    AP_DEBUG_ASSERT(session);
+    
+    /* Check that any pending window updates are sent. */
+    status = h2_session_update_windows(session);
+    if (status == APR_SUCCESS) {
+        flush_output = 1;
+    }
+    else if (status != APR_EAGAIN) {
+        return status;
+    }
+    
+    if (h2_session_want_write(session)) {
+        status = APR_SUCCESS;
+        int rv = nghttp2_session_send(session->ngh2);
+        if (rv != 0) {
+            ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                          "h2_session: send: %s", nghttp2_strerror(rv));
+            if (nghttp2_is_fatal(rv)) {
+                h2_session_abort_int(session, rv);
+                status = APR_ECONNABORTED;
+            }
+        }
+        flush_output = 1;
+    }
+    
+    /* If we have responses ready, submit them now. */
+    while ((stream = h2_mplx_next_submit(session->mplx, 
+                                         session->streams)) != NULL) {
+        status = h2_session_handle_response(session, stream);
+        flush_output = 1;
+    }
+    
+    if (h2_session_resume_streams_with_data(session) > 0) {
+        flush_output = 1;
+    }
+    
+    if (!flush_output && timeout > 0 && !h2_session_want_write(session)) {
+        status = h2_mplx_out_trywait(session->mplx, timeout, session->iowait);
+
+        if (status != APR_TIMEUP
+            && h2_session_resume_streams_with_data(session) > 0) {
+            flush_output = 1;
+        }
+        else {
+            /* nothing happened to ongoing streams, do some house-keeping */
+        }
+    }
+    
+    if (h2_session_want_write(session)) {
+        status = APR_SUCCESS;
+        int rv = nghttp2_session_send(session->ngh2);
+        if (rv != 0) {
+            ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                          "h2_session: send2: %s", nghttp2_strerror(rv));
+            if (nghttp2_is_fatal(rv)) {
+                h2_session_abort_int(session, rv);
+                status = APR_ECONNABORTED;
+            }
+        }
+        flush_output = 1;
+    }
+    
+    if (flush_output) {
+        h2_conn_io_flush(&session->io);
+    }
+    
+    return status;
+}
+
+h2_stream *h2_session_get_stream(h2_session *session, int stream_id)
+{
+    AP_DEBUG_ASSERT(session);
+    return h2_stream_set_get(session->streams, stream_id);
+}
+
+/* h2_io_on_read_cb implementation that offers the data read
+ * directly to the session for consumption.
+ */
+static apr_status_t session_receive(const char *data, apr_size_t len,
+                                    apr_size_t *readlen, int *done,
+                                    void *puser)
+{
+    h2_session *session = (h2_session *)puser;
+    AP_DEBUG_ASSERT(session);
+    if (len > 0) {
+        ssize_t n = nghttp2_session_mem_recv(session->ngh2,
+                                             (const uint8_t *)data, len);
+        if (n < 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL,
+                          session->c,
+                          "h2_session: nghttp2_session_mem_recv error %d",
+                          (int)n);
+            if (nghttp2_is_fatal((int)n)) {
+                *done = 1;
+                h2_session_abort_int(session, (int)n);
+                return APR_EGENERAL;
+            }
+        }
+        else {
+            *readlen = n;
+        }
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_session_read(h2_session *session, apr_read_type_e block)
+{
+    AP_DEBUG_ASSERT(session);
+    return h2_conn_io_read(&session->io, block, session_receive, session);
+}
+
+apr_status_t h2_session_close(h2_session *session)
+{
+    AP_DEBUG_ASSERT(session);
+    return h2_conn_io_flush(&session->io);
+}
+
+/* The session wants to send more DATA for the given stream.
+ */
+static ssize_t stream_data_cb(nghttp2_session *ng2s,
+                              int32_t stream_id,
+                              uint8_t *buf,
+                              size_t length,
+                              uint32_t *data_flags,
+                              nghttp2_data_source *source,
+                              void *puser)
+{
+    h2_session *session = (h2_session *)puser;
+    AP_DEBUG_ASSERT(session);
+    (void)ng2s;(void)source;
+    
+    h2_stream *stream = h2_stream_set_get(session->streams, stream_id);
+    if (!stream) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
+                      "h2_stream(%ld-%d): data requested but stream not found",
+                      session->id, (int)stream_id);
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    
+    AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
+    apr_size_t nread = length;
+    int eos = 0;
+    
+#if NGHTTP2_HAS_DATA_CB
+    apr_status_t status = h2_stream_prep_read(stream, &nread, &eos);
+    if (nread) {
+        *data_flags |=  NGHTTP2_DATA_FLAG_NO_COPY;
+    }
+#else
+    /* Try to pop data buckets from our queue for this stream
+     * until we see EOS or the buffer is full.
+     */
+    apr_status_t status = h2_stream_read(stream, (char*)buf, &nread, &eos);
+#endif
+    
+    switch (status) {
+        case APR_SUCCESS:
+            break;
+            
+        case APR_EAGAIN:
+            /* If there is no data available, our session will automatically
+             * suspend this stream and not ask for more data until we resume
+             * it. Remember at our h2_stream that we need to do this.
+             */
+            nread = 0;
+            h2_stream_set_suspended(stream, 1);
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                          "h2_stream(%ld-%d): suspending stream",
+                          session->id, (int)stream_id);
+            return NGHTTP2_ERR_DEFERRED;
+            
+        case APR_EOF:
+            nread = 0;
+            eos = 1;
+            break;
+            
+        default:
+            nread = 0;
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
+                          "h2_stream(%ld-%d): reading data",
+                          session->id, (int)stream_id);
+            return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    
+    if (eos) {
+        *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+    }
+    
+    return (ssize_t)nread;
+}
+
+typedef struct {
+    nghttp2_nv *nv;
+    size_t nvlen;
+    size_t offset;
+} nvctx_t;
+
+static int submit_response(h2_session *session, h2_response *response)
+{
+    nghttp2_data_provider provider = {
+        (nghttp2_data_source) response->stream_id,
+        (nghttp2_data_source_read_callback) stream_data_cb
+    };
+    
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
+                  "h2_stream(%ld-%d): submitting response %s",
+                  session->id, response->stream_id, response->headers->status);
+    
+    int rv = nghttp2_submit_response(session->ngh2, response->stream_id,
+                                     response->headers->nv, 
+                                     response->headers->nvlen, &provider);
+    
+    if (rv != 0) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
+                      "h2_stream(%ld-%d): submit_response: %s",
+                      session->id, response->stream_id, nghttp2_strerror(rv));
+    }
+    else {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                      "h2_stream(%ld-%d): submitted response %s, rv=%d",
+                      session->id, response->stream_id, 
+                      response->headers->status, rv);
+    }
+    return rv;
+}
+
+/* Start submitting the response to a stream request. This is possible
+ * once we have all the response headers. The response body will be
+ * read by the session using the callback we supply.
+ */
+apr_status_t h2_session_handle_response(h2_session *session, h2_stream *stream)
+{
+    AP_DEBUG_ASSERT(session);
+    AP_DEBUG_ASSERT(stream);
+    AP_DEBUG_ASSERT(stream->response);
+    
+    apr_status_t status = APR_SUCCESS;
+    int rv = 0;
+    if (stream->response->headers) {
+        rv = submit_response(session, stream->response);
+    }
+    else {
+        rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
+                                       stream->id, NGHTTP2_PROTOCOL_ERROR);
+    }
+    
+    if (nghttp2_is_fatal(rv)) {
+        status = APR_EGENERAL;
+        h2_session_abort_int(session, rv);
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
+                      "submit_response: %s", nghttp2_strerror(rv));
+    }
+    return status;
+}
+
+int h2_session_is_done(h2_session *session)
+{
+    AP_DEBUG_ASSERT(session);
+    return (session->aborted
+            || !session->ngh2
+            || (!nghttp2_session_want_read(session->ngh2)
+                && !nghttp2_session_want_write(session->ngh2)));
+}
+
+static int log_stream(void *ctx, h2_stream *stream)
+{
+    h2_session *session = (h2_session *)ctx;
+    AP_DEBUG_ASSERT(session);
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
+                  "h2_stream(%ld-%d): in set, suspended=%d, aborted=%d, "
+                  "has_data=%d",
+                  session->id, stream->id, stream->suspended, stream->aborted,
+                  h2_mplx_out_has_data_for(session->mplx, stream->id));
+    return 1;
+}
+
+void h2_session_log_stats(h2_session *session)
+{
+    AP_DEBUG_ASSERT(session);
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
+                  "h2_session(%ld): %d open streams",
+                  session->id, (int)h2_stream_set_size(session->streams));
+    h2_stream_set_iter(session->streams, log_stream, session);
+}
+
+static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
+{
+    char scratch[128];
+    size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+    
+    switch (frame->hd.type) {
+        case NGHTTP2_DATA: {
+            return apr_snprintf(buffer, maxlen,
+                                "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
+                                (int)frame->hd.length, frame->hd.flags,
+                                frame->hd.stream_id, (int)frame->data.padlen);
+        }
+        case NGHTTP2_HEADERS: {
+            return apr_snprintf(buffer, maxlen,
+                                "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
+                                (int)frame->hd.length,
+                                !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+                                frame->hd.stream_id,
+                                !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
+        }
+        case NGHTTP2_PRIORITY: {
+            return apr_snprintf(buffer, maxlen,
+                                "PRIORITY[length=%d, flags=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                frame->hd.flags, frame->hd.stream_id);
+        }
+        case NGHTTP2_RST_STREAM: {
+            return apr_snprintf(buffer, maxlen,
+                                "RST_STREAM[length=%d, flags=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                frame->hd.flags, frame->hd.stream_id);
+        }
+        case NGHTTP2_SETTINGS: {
+            if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
+                return apr_snprintf(buffer, maxlen,
+                                    "SETTINGS[ack=1, stream=%d]",
+                                    frame->hd.stream_id);
+            }
+            return apr_snprintf(buffer, maxlen,
+                                "SETTINGS[length=%d, stream=%d]",
+                                (int)frame->hd.length, frame->hd.stream_id);
+        }
+        case NGHTTP2_PUSH_PROMISE: {
+            return apr_snprintf(buffer, maxlen,
+                                "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+                                frame->hd.stream_id);
+        }
+        case NGHTTP2_PING: {
+            return apr_snprintf(buffer, maxlen,
+                                "PING[length=%d, ack=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                frame->hd.flags&NGHTTP2_FLAG_ACK,
+                                frame->hd.stream_id);
+        }
+        case NGHTTP2_GOAWAY: {
+            size_t len = (frame->goaway.opaque_data_len < s_len)?
+            frame->goaway.opaque_data_len : s_len-1;
+            memcpy(scratch, frame->goaway.opaque_data, len);
+            scratch[len+1] = '\0';
+            return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
+                                frame->goaway.error_code, scratch);
+        }
+        case NGHTTP2_WINDOW_UPDATE: {
+            return apr_snprintf(buffer, maxlen,
+                                "WINDOW_UPDATE[length=%d, stream=%d]",
+                                (int)frame->hd.length, frame->hd.stream_id);
+        }
+        default:
+            return apr_snprintf(buffer, maxlen,
+                                "FRAME[type=%d, length=%d, flags=%d, stream=%d]",
+                                frame->hd.type, (int)frame->hd.length,
+                                frame->hd.flags, frame->hd.stream_id);
+    }
+}
+
diff --git a/modules/http2/mod_h2/h2_session.h b/modules/http2/mod_h2/h2_session.h
new file mode 100644 (file)
index 0000000..77dd440
--- /dev/null
@@ -0,0 +1,137 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_session__
+#define __mod_h2__h2_session__
+
+#include "h2_conn_io.h"
+
+/**
+ * A HTTP/2 connection, a session with a specific client.
+ * 
+ * h2_session sits on top of a httpd conn_rec* instance and takes complete
+ * control of the connection data. It receives protocol frames from the
+ * client. For new HTTP/2 streams it creates h2_task(s) that are sent
+ * via callback to a dispatcher (see h2_conn.c).
+ * h2_session keeps h2_io's for each ongoing stream which buffer the
+ * payload for that stream.
+ *
+ * New incoming HEADER frames are converted into a h2_stream+h2_task instance
+ * that both represent a HTTP/2 stream, but may have separate lifetimes. This
+ * allows h2_task to be scheduled in other threads without semaphores
+ * all over the place. It allows task memory to be freed independant of
+ * session lifetime and sessions may close down while tasks are still running.
+ *
+ *
+ */
+
+struct apr_thread_mutext_t;
+struct apr_thread_cond_t;
+struct h2_config;
+struct h2_mplx;
+struct h2_response;
+struct h2_session;
+struct h2_stream;
+struct h2_task;
+struct h2_workers;
+
+struct nghttp2_session;
+
+typedef struct h2_session h2_session;
+
+struct h2_session {
+    long id;                        /* identifier of this session, unique
+                                     * inside a httpd process */
+    conn_rec *c;                    /* the connection this session serves */
+    request_rec *r;                 /* the request that started this in case
+                                     * of 'h2c', NULL otherwise */
+    int aborted;                    /* this session is being aborted */
+    apr_size_t frames_received;     /* number of http/2 frames received */
+    apr_size_t max_stream_count;    /* max number of open streams */
+    apr_size_t max_stream_mem;      /* max buffer memory for a single stream */
+    
+    apr_pool_t *pool;               /* pool to use in session handling */
+    apr_bucket_brigade *bbtmp;      /* brigade for keeping temporary data */
+    struct apr_thread_cond_t *iowait; /* our cond when trywaiting for data */
+    
+    h2_conn_io io;                  /* io on httpd conn filters */
+    struct h2_mplx *mplx;           /* multiplexer for stream data */
+    
+    struct h2_stream_set *streams;  /* streams handled by this session */
+    
+    struct nghttp2_session *ngh2;   /* the nghttp2 session (internal use) */
+    struct h2_workers *workers;     /* for executing stream tasks */
+};
+
+
+/* Create a new h2_session for the given connection (mode 'h2').
+ * The session will apply the configured parameter.
+ */
+h2_session *h2_session_create(conn_rec *c, struct h2_config *cfg, 
+                              struct h2_workers *workers);
+
+/* Create a new h2_session for the given request (mode 'h2c').
+ * The session will apply the configured parameter.
+ */
+h2_session *h2_session_rcreate(request_rec *r, struct h2_config *cfg,
+                               struct h2_workers *workers);
+
+/* Destroy the session and all object it still contains. This will not
+ * destroy h2_task instances that not finished yet. */
+void h2_session_destroy(h2_session *session);
+
+/* Called once at start of session. Performs initial client thingies. */
+apr_status_t h2_session_start(h2_session *session, int *rv);
+
+/* Return != 0 iff session is finished and connection can be closed.
+ */
+int h2_session_is_done(h2_session *session);
+
+/* Called when the session will shutdown after all open streams
+ * are handled. New streams will no longer be accepted. 
+ * Call with reason APR_SUCCESS to initiate a graceful shutdown. */
+apr_status_t h2_session_goaway(h2_session *session, apr_status_t reason);
+
+/* Called when an error occured and the session needs to shut down.
+ * Status indicates the reason of the error. */
+apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv);
+
+/* Called before a session gets destroyed, might flush output etc. */
+apr_status_t h2_session_close(h2_session *session);
+
+/* Read more data from the client connection. Used normally with blocking
+ * APR_NONBLOCK_READ, which will return APR_EAGAIN when no data is available.
+ * Use with APR_BLOCK_READ only when certain that no data needs to be written
+ * while waiting. */
+apr_status_t h2_session_read(h2_session *session, apr_read_type_e block);
+
+/* Write data out to the client, if there is any. Otherwise, wait for
+ * a maximum of timeout micro-seconds and return to the caller. If timeout
+ * occurred, APR_TIMEUP will be returned.
+ */
+apr_status_t h2_session_write(h2_session *session,
+                              apr_interval_time_t timeout);
+
+/* Start submitting the response to a stream request. This is possible
+ * once we have all the response headers. */
+apr_status_t h2_session_handle_response(h2_session *session,
+                                        struct h2_stream *stream);
+
+/* Get the h2_stream for the given stream idenrtifier. */
+struct h2_stream *h2_session_get_stream(h2_session *session, int stream_id);
+
+void h2_session_log_stats(h2_session *session);
+
+#endif /* defined(__mod_h2__h2_session__) */
diff --git a/modules/http2/mod_h2/h2_stream.c b/modules/http2/mod_h2/h2_stream.c
new file mode 100644 (file)
index 0000000..29add7b
--- /dev/null
@@ -0,0 +1,297 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#define APR_POOL_DEBUG  7
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_connection.h>
+#include <http_log.h>
+
+#include <nghttp2/nghttp2.h>
+
+#include "h2_private.h"
+#include "h2_conn.h"
+#include "h2_mplx.h"
+#include "h2_request.h"
+#include "h2_response.h"
+#include "h2_stream.h"
+#include "h2_task.h"
+#include "h2_ctx.h"
+#include "h2_task_input.h"
+#include "h2_task.h"
+#include "h2_util.h"
+
+
+static void set_state(h2_stream *stream, h2_stream_state_t state)
+{
+    AP_DEBUG_ASSERT(stream);
+    if (stream->state != state) {
+        stream->state = state;
+    }
+}
+
+h2_stream *h2_stream_create(int id, apr_pool_t *pool, struct h2_mplx *m)
+{
+    h2_stream *stream = apr_pcalloc(pool, sizeof(h2_stream));
+    if (stream != NULL) {
+        stream->id = id;
+        stream->state = H2_STREAM_ST_IDLE;
+        stream->pool = pool;
+        stream->m = m;
+        stream->request = h2_request_create(id, pool, m->c->bucket_alloc);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
+                      "h2_stream(%ld-%d): created", m->id, stream->id);
+    }
+    return stream;
+}
+
+void h2_stream_cleanup(h2_stream *stream)
+{
+    if (stream->request) {
+        h2_request_destroy(stream->request);
+        stream->request = NULL;
+    }
+}
+
+apr_status_t h2_stream_destroy(h2_stream *stream)
+{
+    AP_DEBUG_ASSERT(stream);
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->m->c,
+                  "h2_stream(%ld-%d): destroy", stream->m->id, stream->id);
+    h2_stream_cleanup(stream);
+    
+    if (stream->task) {
+        h2_task_destroy(stream->task);
+        stream->task = NULL;
+    }
+    if (stream->pool) {
+        apr_pool_destroy(stream->pool);
+    }
+    return APR_SUCCESS;
+}
+
+void h2_stream_attach_pool(h2_stream *stream, apr_pool_t *pool)
+{
+    stream->pool = pool;
+}
+
+apr_pool_t *h2_stream_detach_pool(h2_stream *stream)
+{
+    apr_pool_t *pool = stream->pool;
+    stream->pool = NULL;
+    return pool;
+}
+
+void h2_stream_abort(h2_stream *stream)
+{
+    AP_DEBUG_ASSERT(stream);
+    stream->aborted = 1;
+}
+
+apr_status_t h2_stream_set_response(h2_stream *stream, h2_response *response,
+                                    apr_bucket_brigade *bb)
+{
+    stream->response = response;
+    if (bb && !APR_BRIGADE_EMPTY(bb)) {
+        if (!stream->bbout) {
+            stream->bbout = apr_brigade_create(stream->pool, 
+                                               stream->m->c->bucket_alloc);
+        }
+        return h2_util_move(stream->bbout, bb, 16 * 1024, 1, NULL, 
+                            "h2_stream_set_response");
+    }
+    return APR_SUCCESS;
+}
+
+static int set_closed(h2_stream *stream) 
+{
+    switch (stream->state) {
+        case H2_STREAM_ST_CLOSED_INPUT:
+        case H2_STREAM_ST_CLOSED:
+            return 0; /* ignore, idempotent */
+        case H2_STREAM_ST_CLOSED_OUTPUT:
+            /* both closed now */
+            set_state(stream, H2_STREAM_ST_CLOSED);
+            break;
+        default:
+            /* everything else we jump to here */
+            set_state(stream, H2_STREAM_ST_CLOSED_INPUT);
+            break;
+    }
+    return 1;
+}
+
+apr_status_t h2_stream_rwrite(h2_stream *stream, request_rec *r)
+{
+    AP_DEBUG_ASSERT(stream);
+    set_state(stream, H2_STREAM_ST_OPEN);
+    apr_status_t status = h2_request_rwrite(stream->request, r, stream->m);
+    return status;
+}
+
+apr_status_t h2_stream_write_eoh(h2_stream *stream, int eos)
+{
+    AP_DEBUG_ASSERT(stream);
+    /* Seeing the end-of-headers, we have everything we need to 
+     * start processing it.
+     */
+    conn_rec *c = h2_conn_create(stream->m->c, stream->pool);
+    if (c == NULL) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, stream->m->c,
+                      "h2_stream(%ld-%d): create connection",
+                      stream->m->id, stream->id);
+        return APR_ENOMEM;
+    }
+    stream->task = h2_task_create(stream->m->id, stream->id, 
+                                  stream->pool, stream->m, c);
+    
+    apr_status_t status = h2_request_end_headers(stream->request, 
+                                                 stream->m, stream->task, eos);
+    if (status == APR_SUCCESS) {
+        status = h2_mplx_do_task(stream->m, stream->task);
+    }
+    if (eos) {
+        status = h2_stream_write_eos(stream);
+    }
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->m->c,
+                  "h2_stream(%ld-%d): end header, task %s %s (%s)",
+                  stream->m->id, stream->id,
+                  stream->request->method, stream->request->path,
+                  stream->request->authority);
+    
+    return status;
+}
+
+apr_status_t h2_stream_write_eos(h2_stream *stream)
+{
+    AP_DEBUG_ASSERT(stream);
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->m->c,
+                  "h2_stream(%ld-%d): closing input",
+                  stream->m->id, stream->id);
+    if (set_closed(stream)) {
+        return h2_request_close(stream->request);
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_stream_write_header(h2_stream *stream,
+                                    const char *name, size_t nlen,
+                                    const char *value, size_t vlen)
+{
+    AP_DEBUG_ASSERT(stream);
+    switch (stream->state) {
+        case H2_STREAM_ST_IDLE:
+            set_state(stream, H2_STREAM_ST_OPEN);
+            break;
+        case H2_STREAM_ST_OPEN:
+            break;
+        default:
+            return APR_EINVAL;
+    }
+    return h2_request_write_header(stream->request, name, nlen,
+                                   value, vlen, stream->m);
+}
+
+apr_status_t h2_stream_write_data(h2_stream *stream,
+                                  const char *data, size_t len)
+{
+    AP_DEBUG_ASSERT(stream);
+    AP_DEBUG_ASSERT(stream);
+    switch (stream->state) {
+        case H2_STREAM_ST_OPEN:
+            break;
+        default:
+            return APR_EINVAL;
+    }
+    return h2_request_write_data(stream->request, data, len);
+}
+
+apr_status_t h2_stream_prep_read(h2_stream *stream, 
+                                 apr_size_t *plen, int *peos)
+{
+    apr_status_t status = APR_SUCCESS;
+    const char *src;
+    
+    if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) {
+        src = "stream";
+        status = h2_util_bb_avail(stream->bbout, plen, peos);
+        if (status == APR_SUCCESS && !*peos && !*plen) {
+            apr_brigade_cleanup(stream->bbout);
+            return h2_stream_prep_read(stream, plen, peos);
+        }
+    }
+    else {
+        src = "mplx";
+        status = h2_mplx_out_readx(stream->m, stream->id, 
+                                   NULL, NULL, plen, peos);
+    }
+    if (status == APR_SUCCESS && !*peos && !*plen) {
+        status = APR_EAGAIN;
+    }
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->m->c,
+                  "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d",
+                  stream->m->id, stream->id, 
+                  src, (long)*plen, *peos);
+    return status;
+}
+
+apr_status_t h2_stream_readx(h2_stream *stream, 
+                             h2_io_data_cb *cb, void *ctx,
+                             apr_size_t *plen, int *peos)
+{
+    if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) {
+        return h2_util_bb_readx(stream->bbout, cb, ctx, plen, peos);
+    }
+    return h2_mplx_out_readx(stream->m, stream->id, 
+                             cb, ctx, plen, peos);
+}
+
+
+apr_status_t h2_stream_read(h2_stream *stream, char *buffer, 
+                            apr_size_t *plen, int *peos)
+{
+    apr_status_t status = APR_SUCCESS;
+    const char *src;
+    if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) {
+        src = "stream";
+        status = h2_util_bb_read(stream->bbout, buffer, plen, peos);
+    }
+    else {
+        src = "mplx";
+        status = h2_mplx_out_read(stream->m, stream->id, buffer, plen, peos);
+    }
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->m->c,
+                  "h2_stream(%ld-%d): read %s, len=%ld eos=%d",
+                  stream->m->id, stream->id, 
+                  src, (long)*plen, *peos);
+    return status;
+}
+
+void h2_stream_set_suspended(h2_stream *stream, int suspended)
+{
+    AP_DEBUG_ASSERT(stream);
+    stream->suspended = !!suspended;
+}
+
+int h2_stream_is_suspended(h2_stream *stream)
+{
+    AP_DEBUG_ASSERT(stream);
+    return stream->suspended;
+}
+
diff --git a/modules/http2/mod_h2/h2_stream.h b/modules/http2/mod_h2/h2_stream.h
new file mode 100644 (file)
index 0000000..9df2886
--- /dev/null
@@ -0,0 +1,111 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_stream__
+#define __mod_h2__h2_stream__
+
+/**
+ * A HTTP/2 stream, e.g. a client request+response in HTTP/1.1 terms.
+ * 
+ * Ok, not quite, but close enough, since we do not implement server
+ * pushes yet.
+ *
+ * A stream always belongs to a h2_session, the one managing the
+ * connection to the client. The h2_session writes to the h2_stream,
+ * adding HEADERS and DATA and finally an EOS. When headers are done,
+ * h2_stream can create a h2_task that can be scheduled to fullfill the
+ * request.
+ * 
+ * This response headers are added directly to the h2_mplx of the session,
+ * but the response DATA can be read via h2_stream. Reading data will
+ * never block but return APR_EAGAIN when there currently is no data (and
+ * no eos) in the multiplexer for this stream.
+ */
+#include "h2_io.h"
+
+typedef enum {
+    H2_STREAM_ST_IDLE,
+    H2_STREAM_ST_OPEN,
+    H2_STREAM_ST_RESV_LOCAL,
+    H2_STREAM_ST_RESV_REMOTE,
+    H2_STREAM_ST_CLOSED_INPUT,
+    H2_STREAM_ST_CLOSED_OUTPUT,
+    H2_STREAM_ST_CLOSED,
+} h2_stream_state_t;
+
+struct h2_mplx;
+struct h2_request;
+struct h2_response;
+struct h2_task;
+
+typedef struct h2_stream h2_stream;
+
+struct h2_stream {
+    int id;                     /* http2 stream id */
+    h2_stream_state_t state;    /* http/2 state of this stream */
+    struct h2_mplx *m;          /* the multiplexer to work with */
+    
+    int aborted;                /* was aborted */
+    int suspended;              /* DATA sending has been suspended */
+    
+    apr_pool_t *pool;           /* the memory pool for this stream */
+    struct h2_request *request; /* the request made in this stream */
+    
+    struct h2_task *task;       /* task created for this stream */
+    struct h2_response *response; /* the response, once ready */
+    apr_bucket_brigade *bbout;  /* output DATA */
+};
+
+
+h2_stream *h2_stream_create(int id, apr_pool_t *pool, struct h2_mplx *m);
+
+apr_status_t h2_stream_destroy(h2_stream *stream);
+void h2_stream_cleanup(h2_stream *stream);
+
+apr_pool_t *h2_stream_detach_pool(h2_stream *stream);
+void h2_stream_attach_pool(h2_stream *stream, apr_pool_t *pool);
+
+void h2_stream_abort(h2_stream *stream);
+
+apr_status_t h2_stream_rwrite(h2_stream *stream, request_rec *r);
+
+apr_status_t h2_stream_write_eos(h2_stream *stream);
+
+apr_status_t h2_stream_write_header(h2_stream *stream,
+                                    const char *name, size_t nlen,
+                                    const char *value, size_t vlen);
+
+apr_status_t h2_stream_write_eoh(h2_stream *stream, int eos);
+
+apr_status_t h2_stream_write_data(h2_stream *stream,
+                                  const char *data, size_t len);
+
+apr_status_t h2_stream_set_response(h2_stream *stream, 
+                                    struct h2_response *response,
+                                    apr_bucket_brigade *bb);
+
+apr_status_t h2_stream_read(h2_stream *stream, char *buffer, 
+                            apr_size_t *plen, int *peos);
+
+apr_status_t h2_stream_prep_read(h2_stream *stream, 
+                                 apr_size_t *plen, int *peos);
+
+apr_status_t h2_stream_readx(h2_stream *stream, h2_io_data_cb *cb, 
+                             void *ctx, apr_size_t *plen, int *peos);
+
+void h2_stream_set_suspended(h2_stream *stream, int suspended);
+int h2_stream_is_suspended(h2_stream *stream);
+
+#endif /* defined(__mod_h2__h2_stream__) */
diff --git a/modules/http2/mod_h2/h2_stream_set.c b/modules/http2/mod_h2/h2_stream_set.c
new file mode 100644 (file)
index 0000000..bf7fd62
--- /dev/null
@@ -0,0 +1,158 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_connection.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_session.h"
+#include "h2_stream.h"
+#include "h2_task.h"
+#include "h2_stream_set.h"
+
+#define H2_STREAM_IDX(list, i) ((h2_stream**)(list)->elts)[i]
+
+struct h2_stream_set {
+    apr_array_header_t *list;
+};
+
+h2_stream_set *h2_stream_set_create(apr_pool_t *pool)
+{
+    h2_stream_set *sp = apr_pcalloc(pool, sizeof(h2_stream_set));
+    if (sp) {
+        sp->list = apr_array_make(pool, 100, sizeof(h2_stream*));
+        if (!sp->list) {
+            return NULL;
+        }
+    }
+    return sp;
+}
+
+void h2_stream_set_destroy(h2_stream_set *sp)
+{
+    (void)sp;
+}
+
+static int h2_stream_id_cmp(const void *s1, const void *s2)
+{
+    h2_stream **pstream1 = (h2_stream **)s1;
+    h2_stream **pstream2 = (h2_stream **)s2;
+    return (*pstream1)->id - (*pstream2)->id;
+}
+
+h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id)
+{
+    /* we keep the array sorted by id, so lookup can be done
+     * by bsearch.
+     */
+    h2_stream key;
+    memset(&key, 0, sizeof(key));
+    key.id = stream_id;
+    h2_stream *pkey = &key;
+    h2_stream **ps = bsearch(&pkey, sp->list->elts, sp->list->nelts, 
+                             sp->list->elt_size, h2_stream_id_cmp);
+    return ps? *ps : NULL;
+}
+
+static void h2_stream_set_sort(h2_stream_set *sp)
+{
+    qsort(sp->list->elts, sp->list->nelts, sp->list->elt_size, 
+          h2_stream_id_cmp);
+}
+
+apr_status_t h2_stream_set_add(h2_stream_set *sp, h2_stream *stream)
+{
+    h2_stream *existing = h2_stream_set_get(sp, stream->id);
+    if (!existing) {
+        APR_ARRAY_PUSH(sp->list, h2_stream*) = stream;
+        /* Normally, streams get added in ascending order if id. We
+         * keep the array sorted, so we just need to check of the newly
+         * appended stream has a lower id than the last one. if not,
+         * sorting is not necessary.
+         */
+        int last = sp->list->nelts - 1;
+        if (last > 0 
+            && (H2_STREAM_IDX(sp->list, last)->id 
+                < H2_STREAM_IDX(sp->list, last-1)->id)) {
+            h2_stream_set_sort(sp);
+        }
+    }
+    return APR_SUCCESS;
+}
+
+h2_stream *h2_stream_set_remove(h2_stream_set *sp, h2_stream *stream)
+{
+    for (int i = 0; i < sp->list->nelts; ++i) {
+        h2_stream *s = H2_STREAM_IDX(sp->list, i);
+        if (s == stream) {
+            --sp->list->nelts;
+            int n = sp->list->nelts - i;
+            if (n > 0) {
+                /* Close the hole in the array by moving the upper
+                 * parts down one step.
+                 */
+                h2_stream **selts = (h2_stream**)sp->list->elts;
+                memmove(selts+i, selts+i+1, n * sizeof(h2_stream*));
+            }
+            return s;
+        }
+    }
+    return NULL;
+}
+
+void h2_stream_set_remove_all(h2_stream_set *sp)
+{
+    sp->list->nelts = 0;
+}
+
+int h2_stream_set_is_empty(h2_stream_set *sp)
+{
+    AP_DEBUG_ASSERT(sp);
+    return sp->list->nelts == 0;
+}
+
+h2_stream *h2_stream_set_find(h2_stream_set *sp,
+                              h2_stream_set_match_fn match, void *ctx)
+{
+    h2_stream *s = NULL;
+    for (int i = 0; !s && i < sp->list->nelts; ++i) {
+        s = match(ctx, H2_STREAM_IDX(sp->list, i));
+    }
+    return s;
+}
+
+void h2_stream_set_iter(h2_stream_set *sp,
+                        h2_stream_set_iter_fn *iter, void *ctx)
+{
+    for (int i = 0; i < sp->list->nelts; ++i) {
+        h2_stream *s = H2_STREAM_IDX(sp->list, i);
+        if (!iter(ctx, s)) {
+            break;
+        }
+    }
+}
+
+apr_size_t h2_stream_set_size(h2_stream_set *sp)
+{
+    return sp->list->nelts;
+}
+
diff --git a/modules/http2/mod_h2/h2_stream_set.h b/modules/http2/mod_h2/h2_stream_set.h
new file mode 100644 (file)
index 0000000..5607583
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_stream_set__
+#define __mod_h2__h2_stream_set__
+
+/**
+ * A set of h2_stream instances. Allows lookup by stream id
+ * and other criteria.
+ */
+
+typedef h2_stream *h2_stream_set_match_fn(void *ctx, h2_stream *stream);
+typedef int h2_stream_set_iter_fn(void *ctx, h2_stream *stream);
+
+typedef struct h2_stream_set h2_stream_set;
+
+
+h2_stream_set *h2_stream_set_create(apr_pool_t *pool);
+
+void h2_stream_set_destroy(h2_stream_set *sp);
+
+apr_status_t h2_stream_set_add(h2_stream_set *sp, h2_stream *stream);
+
+h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id);
+
+h2_stream *h2_stream_set_remove(h2_stream_set *sp,h2_stream *stream);
+
+void h2_stream_set_remove_all(h2_stream_set *sp);
+
+int h2_stream_set_is_empty(h2_stream_set *sp);
+
+apr_size_t h2_stream_set_size(h2_stream_set *sp);
+
+h2_stream *h2_stream_set_find(h2_stream_set *sp,
+                              h2_stream_set_match_fn *match, void *ctx);
+
+void h2_stream_set_iter(h2_stream_set *sp,
+                        h2_stream_set_iter_fn *iter, void *ctx);
+
+#endif /* defined(__mod_h2__h2_stream_set__) */
diff --git a/modules/http2/mod_h2/h2_task.c b/modules/http2/mod_h2/h2_task.c
new file mode 100644 (file)
index 0000000..bdae25a
--- /dev/null
@@ -0,0 +1,464 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#include <apr_atomic.h>
+#include <apr_thread_cond.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_request.h>
+#include <http_log.h>
+#include <http_vhost.h>
+#include <util_filter.h>
+#include <ap_mpm.h>
+#include <mod_core.h>
+#include <scoreboard.h>
+
+#include "h2_private.h"
+#include "h2_conn.h"
+#include "h2_config.h"
+#include "h2_from_h1.h"
+#include "h2_h2.h"
+#include "h2_mplx.h"
+#include "h2_session.h"
+#include "h2_stream.h"
+#include "h2_task_input.h"
+#include "h2_task_output.h"
+#include "h2_task.h"
+#include "h2_ctx.h"
+#include "h2_worker.h"
+
+
+static apr_status_t h2_filter_stream_input(ap_filter_t* filter,
+                                           apr_bucket_brigade* brigade,
+                                           ap_input_mode_t mode,
+                                           apr_read_type_e block,
+                                           apr_off_t readbytes) {
+    h2_task_env *env = filter->ctx;
+    AP_DEBUG_ASSERT(task);
+    if (!env->input) {
+        return APR_ECONNABORTED;
+    }
+    return h2_task_input_read(env->input, filter, brigade,
+                              mode, block, readbytes);
+}
+
+static apr_status_t h2_filter_stream_output(ap_filter_t* filter,
+                                            apr_bucket_brigade* brigade) {
+    h2_task_env *env = filter->ctx;
+    AP_DEBUG_ASSERT(task);
+    if (!env->output) {
+        return APR_ECONNABORTED;
+    }
+    return h2_task_output_write(env->output, filter, brigade);
+}
+
+static apr_status_t h2_filter_read_response(ap_filter_t* f,
+                                            apr_bucket_brigade* bb) {
+    h2_task_env *env = f->ctx;
+    AP_DEBUG_ASSERT(task);
+    if (!env->output || !env->output->from_h1) {
+        return APR_ECONNABORTED;
+    }
+    return h2_from_h1_read_response(env->output->from_h1, f, bb);
+}
+
+/*******************************************************************************
+ * Register various hooks
+ */
+static const char *const mod_ssl[]        = { "mod_ssl.c", NULL};
+static int h2_task_pre_conn(conn_rec* c, void *arg);
+static int h2_task_process_conn(conn_rec* c);
+
+void h2_task_register_hooks(void)
+{
+    /* This hook runs on new connections before mod_ssl has a say.
+     * Its purpose is to prevent mod_ssl from touching our pseudo-connections
+     * for streams.
+     */
+    ap_hook_pre_connection(h2_task_pre_conn,
+                           NULL, mod_ssl, APR_HOOK_FIRST);
+    /* When the connection processing actually starts, we might to
+     * take over, if the connection is for a task.
+     */
+    ap_hook_process_connection(h2_task_process_conn, 
+                               NULL, NULL, APR_HOOK_FIRST);
+
+    ap_register_output_filter("H2_RESPONSE", h2_response_output_filter,
+                              NULL, AP_FTYPE_PROTOCOL);
+    ap_register_input_filter("H2_TO_H1", h2_filter_stream_input,
+                             NULL, AP_FTYPE_NETWORK);
+    ap_register_output_filter("H1_TO_H2", h2_filter_stream_output,
+                              NULL, AP_FTYPE_NETWORK);
+    ap_register_output_filter("H1_TO_H2_RESP", h2_filter_read_response,
+                              NULL, AP_FTYPE_PROTOCOL);
+}
+
+static int h2_task_pre_conn(conn_rec* c, void *arg)
+{
+    (void)arg;
+    
+    h2_ctx *ctx = h2_ctx_get(c);
+    if (h2_ctx_is_task(ctx)) {
+        h2_task_env *env = h2_ctx_get_task(ctx);
+        
+        /* This connection is a pseudo-connection used for a h2_task.
+         * Since we read/write directly from it ourselves, we need
+         * to disable a possible ssl connection filter.
+         */
+        h2_tls_disable(c);
+        
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                      "h2_h2, pre_connection, found stream task");
+        
+        /* Add our own, network level in- and output filters.
+         */
+        ap_add_input_filter("H2_TO_H1", env, NULL, c);
+        ap_add_output_filter("H1_TO_H2", env, NULL, c);
+    }
+    return OK;
+}
+
+static int h2_task_process_conn(conn_rec* c)
+{
+    h2_ctx *ctx = h2_ctx_get(c);
+    
+    if (h2_ctx_is_task(ctx)) {
+        if (!ctx->task_env->serialize_headers) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
+                          "h2_h2, processing request directly");
+            h2_task_process_request(ctx->task_env);
+            return DONE;
+        }
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
+                      "h2_task(%s), serialized handling", ctx->task_env->id);
+    }
+    return DECLINED;
+}
+
+
+h2_task *h2_task_create(long session_id,
+                        int stream_id,
+                        apr_pool_t *stream_pool,
+                        h2_mplx *mplx, conn_rec *c)
+{
+    h2_task *task = apr_pcalloc(stream_pool, sizeof(h2_task));
+    if (task == NULL) {
+        ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, stream_pool,
+                      "h2_task(%ld-%d): create stream task", 
+                      session_id, stream_id);
+        h2_mplx_out_close(mplx, stream_id);
+        return NULL;
+    }
+    
+    APR_RING_ELEM_INIT(task, link);
+
+    task->id = apr_psprintf(stream_pool, "%ld-%d", session_id, stream_id);
+    task->stream_id = stream_id;
+    task->mplx = mplx;
+    
+    task->c = c;
+    
+    ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, stream_pool,
+                  "h2_task(%s): created", task->id);
+    return task;
+}
+
+void h2_task_set_request(h2_task *task, 
+                         const char *method, const char *path, 
+                         const char *authority, apr_table_t *headers, int eos)
+{
+    task->method = method;
+    task->path = path;
+    task->authority = authority;
+    task->headers = headers;
+    task->input_eos = eos;
+}
+
+apr_status_t h2_task_destroy(h2_task *task)
+{
+    (void)task;
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_task_do(h2_task *task, h2_worker *worker)
+{
+    apr_status_t status = APR_SUCCESS;
+    h2_config *cfg = h2_config_get(task->mplx->c);
+    h2_task_env env; 
+    
+    AP_DEBUG_ASSERT(task);
+    
+    memset(&env, 0, sizeof(env));
+    
+    env.id = task->id;
+    env.stream_id = task->stream_id;
+    env.mplx = task->mplx;
+    task->mplx = NULL;
+    
+    env.input_eos = task->input_eos;
+    env.serialize_headers = !!h2_config_geti(cfg, H2_CONF_SER_HEADERS);
+    
+    /* Create a subpool from the worker one to be used for all things
+     * with life-time of this task_env execution.
+     */
+    apr_pool_create(&env.pool, h2_worker_get_pool(worker));
+
+    /* Link the env to the worker which provides useful things such
+     * as mutex, a socket etc. */
+    env.io = h2_worker_get_cond(worker);
+    
+    /* Clone fields, so that lifetimes become (more) independent. */
+    env.method    = apr_pstrdup(env.pool, task->method);
+    env.path      = apr_pstrdup(env.pool, task->path);
+    env.authority = apr_pstrdup(env.pool, task->authority);
+    env.headers   = apr_table_clone(env.pool, task->headers);
+    
+    /* Setup the pseudo connection to use our own pool and bucket_alloc */
+    if (task->c) {
+        env.c = *task->c;
+        task->c = NULL;
+        status = h2_conn_setup(&env, worker);
+    }
+    else {
+        status = h2_conn_init(&env, worker);
+    }
+    
+    /* save in connection that this one is a pseudo connection, prevents
+     * other hooks from messing with it. */
+    h2_ctx_create_for(&env.c, &env);
+
+    if (status == APR_SUCCESS) {
+        env.input = h2_task_input_create(&env, env.pool, 
+                                         env.c.bucket_alloc);
+        env.output = h2_task_output_create(&env, env.pool,
+                                           env.c.bucket_alloc);
+        status = h2_conn_process(&env.c, h2_worker_get_socket(worker));
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, &env.c,
+                      "h2_task(%s): processing done", env.id);
+    }
+    else {
+        ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, &env.c,
+                      "h2_task(%s): error setting up h2_task_env", env.id);
+    }
+    
+    if (env.input) {
+        h2_task_input_destroy(env.input);
+        env.input = NULL;
+    }
+    
+    if (env.output) {
+        h2_task_output_close(env.output);
+        h2_task_output_destroy(env.output);
+        env.output = NULL;
+    }
+
+    h2_task_set_finished(task);
+    if (env.io) {
+        apr_thread_cond_signal(env.io);
+    }
+    
+    if (env.pool) {
+        apr_pool_destroy(env.pool);
+        env.pool = NULL;
+    }
+    
+    if (env.c.id) {
+        h2_conn_post(&env.c, worker);
+    }
+    
+    h2_mplx_task_done(env.mplx, env.stream_id);
+    
+    return status;
+}
+
+int h2_task_has_started(h2_task *task)
+{
+    AP_DEBUG_ASSERT(task);
+    return apr_atomic_read32(&task->has_started);
+}
+
+void h2_task_set_started(h2_task *task)
+{
+    AP_DEBUG_ASSERT(task);
+    apr_atomic_set32(&task->has_started, 1);
+}
+
+int h2_task_has_finished(h2_task *task)
+{
+    return apr_atomic_read32(&task->has_finished);
+}
+
+void h2_task_set_finished(h2_task *task)
+{
+    apr_atomic_set32(&task->has_finished, 1);
+}
+
+void h2_task_die(h2_task_env *env, int status, request_rec *r)
+{
+    (void)env;
+    ap_die(status, r);
+}
+
+static request_rec *h2_task_create_request(h2_task_env *env)
+{
+    conn_rec *conn = &env->c;
+    request_rec *r;
+    apr_pool_t *p;
+    int access_status = HTTP_OK;    
+    
+    apr_pool_create(&p, conn->pool);
+    apr_pool_tag(p, "request");
+    r = apr_pcalloc(p, sizeof(request_rec));
+    AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
+    r->pool            = p;
+    r->connection      = conn;
+    r->server          = conn->base_server;
+    
+    r->user            = NULL;
+    r->ap_auth_type    = NULL;
+    
+    r->allowed_methods = ap_make_method_list(p, 2);
+    
+    r->headers_in = apr_table_copy(r->pool, env->headers);
+    r->trailers_in     = apr_table_make(r->pool, 5);
+    r->subprocess_env  = apr_table_make(r->pool, 25);
+    r->headers_out     = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->trailers_out    = apr_table_make(r->pool, 5);
+    r->notes           = apr_table_make(r->pool, 5);
+    
+    r->request_config  = ap_create_request_config(r->pool);
+    /* Must be set before we run create request hook */
+    
+    r->proto_output_filters = conn->output_filters;
+    r->output_filters  = r->proto_output_filters;
+    r->proto_input_filters = conn->input_filters;
+    r->input_filters   = r->proto_input_filters;
+    ap_run_create_request(r);
+    r->per_dir_config  = r->server->lookup_defaults;
+    
+    r->sent_bodyct     = 0;                      /* bytect isn't for body */
+    
+    r->read_length     = 0;
+    r->read_body       = REQUEST_NO_BODY;
+    
+    r->status          = HTTP_OK;  /* Until further notice */
+    r->header_only     = 0;
+    r->the_request     = NULL;
+    
+    /* Begin by presuming any module can make its own path_info assumptions,
+     * until some module interjects and changes the value.
+     */
+    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+    
+    r->useragent_addr = conn->client_addr;
+    r->useragent_ip = conn->client_ip;
+    
+    ap_run_pre_read_request(r, conn);
+    
+    /* Time to populate r with the data we have. */
+    r->request_time = apr_time_now();
+    r->the_request = apr_psprintf(r->pool, "%s %s HTTP/1.1", 
+                                  env->method, env->path);
+    r->method = env->method;
+    /* Provide quick information about the request method as soon as known */
+    r->method_number = ap_method_number_of(r->method);
+    if (r->method_number == M_GET && r->method[0] == 'H') {
+        r->header_only = 1;
+    }
+
+    ap_parse_uri(r, env->path);
+    r->protocol = (char*)"HTTP/1.1";
+    r->proto_num = HTTP_VERSION(1, 1);
+    
+    r->hostname = env->authority;
+    
+    /* update what we think the virtual host is based on the headers we've
+     * now read. may update status.
+     */
+    ap_update_vhost_from_headers(r);
+    
+    /* we may have switched to another server */
+    r->per_dir_config = r->server->lookup_defaults;
+    
+    /*
+     * Add the HTTP_IN filter here to ensure that ap_discard_request_body
+     * called by ap_die and by ap_send_error_response works correctly on
+     * status codes that do not cause the connection to be dropped and
+     * in situations where the connection should be kept alive.
+     */
+    ap_add_input_filter_handle(ap_http_input_filter_handle,
+                               NULL, r, r->connection);
+    
+    if (access_status != HTTP_OK
+        || (access_status = ap_run_post_read_request(r))) {
+        /* Request check post hooks failed. An example of this would be a
+         * request for a vhost where h2 is disabled --> 421.
+         */
+        h2_task_die(env, access_status, r);
+        ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+        ap_run_log_transaction(r);
+        r = NULL;
+        goto traceout;
+    }
+    
+    AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, 
+                            (char *)r->uri, (char *)r->server->defn_name, 
+                            r->status);
+    return r;
+traceout:
+    AP_READ_REQUEST_FAILURE((uintptr_t)r);
+    return r;
+}
+
+
+apr_status_t h2_task_process_request(h2_task_env *env)
+{
+    conn_rec *c = &env->c;
+    request_rec *r;
+    conn_state_t *cs = c->cs;
+
+    r = h2_task_create_request(env);
+    if (r && (r->status == HTTP_OK)) {
+        ap_update_child_status(c->sbh, SERVER_BUSY_READ, r);
+        
+        if (cs)
+            cs->state = CONN_STATE_HANDLER;
+        ap_process_request(r);
+        /* After the call to ap_process_request, the
+         * request pool will have been deleted.  We set
+         * r=NULL here to ensure that any dereference
+         * of r that might be added later in this function
+         * will result in a segfault immediately instead
+         * of nondeterministic failures later.
+         */
+        r = NULL;
+    }
+    ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, NULL);
+    c->sbh = NULL;
+
+    return APR_SUCCESS;
+}
+
+
+
+
diff --git a/modules/http2/mod_h2/h2_task.h b/modules/http2/mod_h2/h2_task.h
new file mode 100644 (file)
index 0000000..4130a02
--- /dev/null
@@ -0,0 +1,181 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_task__
+#define __mod_h2__h2_task__
+
+/**
+ * A h2_task fakes a HTTP/1.1 request from the data in a HTTP/2 stream 
+ * (HEADER+CONT.+DATA) the module recieves.
+ *
+ * In order to answer a HTTP/2 stream, we want all Apache httpd infrastructure
+ * to be involved as usual, as if this stream can as a separate HTTP/1.1
+ * request. The basic trickery to do so was derived from google's mod_spdy
+ * source. Basically, we fake a new conn_rec object, even with its own
+ * socket and give it to ap_process_connection().
+ *
+ * Since h2_task instances are executed in separate threads, we may have
+ * different lifetimes than our h2_stream or h2_session instances. Basically,
+ * we would like to be as standalone as possible.
+ *
+ * Finally, to keep certain connection level filters, such as ourselves and
+ * especially mod_ssl ones, from messing with our data, we need a filter
+ * of our own to disble those.
+ */
+
+struct apr_thread_cond_t;
+struct h2_conn;
+struct h2_mplx;
+struct h2_task;
+struct h2_resp_head;
+struct h2_worker;
+
+typedef struct h2_task h2_task;
+
+struct h2_task {
+    /** Links to the rest of the tasks */
+    APR_RING_ENTRY(h2_task) link;
+
+    const char *id;
+    int stream_id;
+    struct h2_mplx *mplx;
+    
+    volatile apr_uint32_t has_started;
+    volatile apr_uint32_t has_finished;
+    
+    const char *method;
+    const char *path;
+    const char *authority;
+    apr_table_t *headers;
+    int input_eos;
+
+    struct conn_rec *c;
+};
+
+typedef struct h2_task_env h2_task_env;
+
+struct h2_task_env {
+    const char *id;
+    int stream_id;
+    struct h2_mplx *mplx;
+    
+    apr_pool_t *pool;              /* pool for task lifetime things */
+    apr_bucket_alloc_t *bucket_alloc;
+    
+    const char *method;
+    const char *path;
+    const char *authority;
+    apr_table_t *headers;
+    int input_eos;
+    
+    int serialize_headers;
+
+    struct conn_rec c;
+    struct h2_task_input *input;
+    struct h2_task_output *output;
+    
+    struct apr_thread_cond_t *io;   /* used to wait for events on */
+};
+
+/**
+ * The magic pointer value that indicates the head of a h2_task list
+ * @param  b The task list
+ * @return The magic pointer value
+ */
+#define H2_TASK_LIST_SENTINEL(b)       APR_RING_SENTINEL((b), h2_task, link)
+
+/**
+ * Determine if the task list is empty
+ * @param b The list to check
+ * @return true or false
+ */
+#define H2_TASK_LIST_EMPTY(b)  APR_RING_EMPTY((b), h2_task, link)
+
+/**
+ * Return the first task in a list
+ * @param b The list to query
+ * @return The first task in the list
+ */
+#define H2_TASK_LIST_FIRST(b)  APR_RING_FIRST(b)
+
+/**
+ * Return the last task in a list
+ * @param b The list to query
+ * @return The last task int he list
+ */
+#define H2_TASK_LIST_LAST(b)   APR_RING_LAST(b)
+
+/**
+ * Insert a single task at the front of a list
+ * @param b The list to add to
+ * @param e The task to insert
+ */
+#define H2_TASK_LIST_INSERT_HEAD(b, e) do {                            \
+    h2_task *ap__b = (e);                                        \
+    APR_RING_INSERT_HEAD((b), ap__b, h2_task, link);   \
+} while (0)
+
+/**
+ * Insert a single task at the end of a list
+ * @param b The list to add to
+ * @param e The task to insert
+ */
+#define H2_TASK_LIST_INSERT_TAIL(b, e) do {                            \
+    h2_task *ap__b = (e);                                      \
+    APR_RING_INSERT_TAIL((b), ap__b, h2_task, link);   \
+} while (0)
+
+/**
+ * Get the next task in the list
+ * @param e The current task
+ * @return The next task
+ */
+#define H2_TASK_NEXT(e)        APR_RING_NEXT((e), link)
+/**
+ * Get the previous task in the list
+ * @param e The current task
+ * @return The previous task
+ */
+#define H2_TASK_PREV(e)        APR_RING_PREV((e), link)
+
+/**
+ * Remove a task from its list
+ * @param e The task to remove
+ */
+#define H2_TASK_REMOVE(e)      APR_RING_REMOVE((e), link)
+
+
+h2_task *h2_task_create(long session_id, int stream_id, 
+                        apr_pool_t *pool, struct h2_mplx *mplx,
+                        conn_rec *c);
+
+apr_status_t h2_task_destroy(h2_task *task);
+
+void h2_task_set_request(h2_task *task, const char *method, const char *path, 
+                         const char *authority, apr_table_t *headers, int eos);
+
+
+apr_status_t h2_task_do(h2_task *task, struct h2_worker *worker);
+apr_status_t h2_task_process_request(h2_task_env *env);
+
+int h2_task_has_started(h2_task *task);
+void h2_task_set_started(h2_task *task);
+int h2_task_has_finished(h2_task *task);
+void h2_task_set_finished(h2_task *task);
+
+void h2_task_register_hooks(void);
+void h2_task_die(h2_task_env *env, int status, request_rec *r);
+
+#endif /* defined(__mod_h2__h2_task__) */
diff --git a/modules/http2/mod_h2/h2_task_input.c b/modules/http2/mod_h2/h2_task_input.c
new file mode 100644 (file)
index 0000000..54a46dc
--- /dev/null
@@ -0,0 +1,216 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+
+#include "h2_private.h"
+#include "h2_conn.h"
+#include "h2_mplx.h"
+#include "h2_session.h"
+#include "h2_stream.h"
+#include "h2_task_input.h"
+#include "h2_task.h"
+#include "h2_util.h"
+
+
+static int is_aborted(ap_filter_t *f)
+{
+    return (f->c->aborted);
+}
+
+static int ser_header(void *ctx, const char *name, const char *value) 
+{
+    h2_task_input *input = (h2_task_input*)ctx;
+    apr_brigade_printf(input->bb, NULL, NULL, "%s: %s\r\n", name, value);
+    return 1;
+}
+
+h2_task_input *h2_task_input_create(h2_task_env *env, apr_pool_t *pool, 
+                                    apr_bucket_alloc_t *bucket_alloc)
+{
+    h2_task_input *input = apr_pcalloc(pool, sizeof(h2_task_input));
+    if (input) {
+        input->env = env;
+        input->bb = NULL;
+        
+        if (env->serialize_headers) {
+            input->bb = apr_brigade_create(pool, bucket_alloc);
+            apr_brigade_printf(input->bb, NULL, NULL, "%s %s HTTP/1.1\r\n", 
+                               env->method, env->path);
+            apr_table_do(ser_header, input, env->headers, NULL);
+            apr_brigade_puts(input->bb, NULL, NULL, "\r\n");
+            if (input->env->input_eos) {
+                APR_BRIGADE_INSERT_TAIL(input->bb, apr_bucket_eos_create(bucket_alloc));
+            }
+        }
+        else if (!input->env->input_eos) {
+            input->bb = apr_brigade_create(pool, bucket_alloc);
+        }
+        else {
+            /* We do not serialize and have eos already, no need to
+             * create a bucket brigade. */
+        }
+        
+        if (APLOGcdebug(&env->c)) {
+            char buffer[1024];
+            apr_size_t len = sizeof(buffer)-1;
+            if (input->bb) {
+                apr_brigade_flatten(input->bb, buffer, &len);
+            }
+            buffer[len] = 0;
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, &env->c,
+                          "h2_task_input(%s): request is: %s", 
+                          env->id, buffer);
+        }
+    }
+    return input;
+}
+
+void h2_task_input_destroy(h2_task_input *input)
+{
+    input->bb = NULL;
+}
+
+apr_status_t h2_task_input_read(h2_task_input *input,
+                                ap_filter_t* f,
+                                apr_bucket_brigade* bb,
+                                ap_input_mode_t mode,
+                                apr_read_type_e block,
+                                apr_off_t readbytes)
+{
+    apr_status_t status = APR_SUCCESS;
+    apr_off_t bblen = 0;
+    
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
+                  "h2_task_input(%s): read, block=%d, mode=%d, readbytes=%ld", 
+                  input->env->id, block, mode, (long)readbytes);
+    
+    if (is_aborted(f)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                      "h2_task_input(%s): is aborted", 
+                      input->env->id);
+        return APR_ECONNABORTED;
+    }
+    
+    if (mode == AP_MODE_INIT) {
+        return APR_SUCCESS;
+    }
+    
+    if (input->bb) {
+        status = apr_brigade_length(input->bb, 1, &bblen);
+        if (status != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, f->c,
+                          "h2_task_input(%s): brigade length fail", 
+                          input->env->id);
+            return status;
+        }
+    }
+    
+    if ((bblen == 0) && input->env->input_eos) {
+        return APR_EOF;
+    }
+    
+    while ((bblen == 0) || (mode == AP_MODE_READBYTES && bblen < readbytes)) {
+        /* Get more data for our stream from mplx.
+         */
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                      "h2_task_input(%s): get more data from mplx, block=%d, "
+                      "readbytes=%ld, queued=%ld",
+                      input->env->id, block, 
+                      (long)readbytes, (long)bblen);
+        
+        /* Although we sometimes get called with APR_NONBLOCK_READs, 
+         we seem to  fill our buffer blocking. Otherwise we get EAGAIN,
+         return that to our caller and everyone throws up their hands,
+         never calling us again. */
+        status = h2_mplx_in_read(input->env->mplx, APR_BLOCK_READ,
+                                 input->env->stream_id, input->bb, 
+                                 input->env->io);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                      "h2_task_input(%s): mplx in read returned",
+                      input->env->id);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        status = apr_brigade_length(input->bb, 1, &bblen);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        if ((bblen == 0) && (block == APR_NONBLOCK_READ)) {
+            return h2_util_has_eos(input->bb, 0)? APR_EOF : APR_EAGAIN;
+        }
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                      "h2_task_input(%s): mplx in read, %ld bytes in brigade",
+                      input->env->id, (long)bblen);
+    }
+    
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                  "h2_task_input(%s): read, mode=%d, block=%d, "
+                  "readbytes=%ld, queued=%ld",
+                  input->env->id, mode, block, 
+                  (long)readbytes, (long)bblen);
+           
+    if (!APR_BRIGADE_EMPTY(input->bb)) {
+        if (mode == AP_MODE_EXHAUSTIVE) {
+            /* return all we have */
+            return h2_util_move(bb, input->bb, readbytes, 0, 
+                                NULL, "task_input_read(exhaustive)");
+        }
+        else if (mode == AP_MODE_READBYTES) {
+            return h2_util_move(bb, input->bb, readbytes, 0, 
+                                NULL, "task_input_read(readbytes)");
+        }
+        else if (mode == AP_MODE_SPECULATIVE) {
+            /* return not more than was asked for */
+            return h2_util_copy(bb, input->bb, readbytes,  
+                                "task_input_read(speculative)");
+        }
+        else if (mode == AP_MODE_GETLINE) {
+            /* we are reading a single LF line, e.g. the HTTP headers */
+            status = apr_brigade_split_line(bb, input->bb, block, 
+                                            HUGE_STRING_LEN);
+            if (APLOGctrace1(f->c)) {
+                char buffer[1024];
+                apr_size_t len = sizeof(buffer)-1;
+                apr_brigade_flatten(bb, buffer, &len);
+                buffer[len] = 0;
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                              "h2_task_input(%s): getline: %s",
+                              input->env->id, buffer);
+            }
+            return status;
+        }
+        else {
+            /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
+             * to support it. Seems to work. */
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
+                          "h2_task_input, unsupported READ mode %d",
+                          mode);
+            return APR_ENOTIMPL;
+        }
+    }
+    
+    if (is_aborted(f)) {
+        return APR_ECONNABORTED;
+    }
+    
+    return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF;
+}
+
diff --git a/modules/http2/mod_h2/h2_task_input.h b/modules/http2/mod_h2/h2_task_input.h
new file mode 100644 (file)
index 0000000..32adc17
--- /dev/null
@@ -0,0 +1,46 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_task_input__
+#define __mod_h2__h2_task_input__
+
+/* h2_task_input places the HEADER+DATA, formatted in HTTP/1.1, into
+ * a bucket brigade. The brigade is setup as the input brigade for our
+ * pseudo httpd conn_rec that is handling a specific h2_task.
+ */
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task_env;
+
+typedef struct h2_task_input h2_task_input;
+struct h2_task_input {
+    struct h2_task_env *env;
+    apr_bucket_brigade *bb;
+};
+
+
+h2_task_input *h2_task_input_create(struct h2_task_env *env, apr_pool_t *pool,
+                                    apr_bucket_alloc_t *bucket_alloc);
+
+void h2_task_input_destroy(h2_task_input *input);
+
+apr_status_t h2_task_input_read(h2_task_input *input,
+                                  ap_filter_t* filter,
+                                  apr_bucket_brigade* brigade,
+                                  ap_input_mode_t mode,
+                                  apr_read_type_e block,
+                                  apr_off_t readbytes);
+
+#endif /* defined(__mod_h2__h2_task_input__) */
diff --git a/modules/http2/mod_h2/h2_task_output.c b/modules/http2/mod_h2/h2_task_output.c
new file mode 100644 (file)
index 0000000..b944d8c
--- /dev/null
@@ -0,0 +1,133 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_thread_cond.h>
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+
+#include "h2_private.h"
+#include "h2_conn.h"
+#include "h2_mplx.h"
+#include "h2_session.h"
+#include "h2_stream.h"
+#include "h2_from_h1.h"
+#include "h2_response.h"
+#include "h2_task_output.h"
+#include "h2_task.h"
+#include "h2_util.h"
+
+
+h2_task_output *h2_task_output_create(h2_task_env *env, apr_pool_t *pool,
+                                      apr_bucket_alloc_t *bucket_alloc)
+{
+    (void)bucket_alloc;
+    h2_task_output *output = apr_pcalloc(pool, sizeof(h2_task_output));
+    if (output) {
+        output->env = env;
+        output->state = H2_TASK_OUT_INIT;
+        output->from_h1 = h2_from_h1_create(env->stream_id, pool);
+        if (!output->from_h1) {
+            return NULL;
+        }
+    }
+    return output;
+}
+
+void h2_task_output_destroy(h2_task_output *output)
+{
+    if (output->from_h1) {
+        h2_from_h1_destroy(output->from_h1);
+        output->from_h1 = NULL;
+    }
+}
+
+static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f,
+                                   apr_bucket_brigade *bb)
+{
+    if (output->state == H2_TASK_OUT_INIT) {
+        output->state = H2_TASK_OUT_STARTED;
+        h2_response *response = h2_from_h1_get_response(output->from_h1);
+        if (!response) {
+            if (f) {
+                /* This happens currently when ap_die(status, r) is invoked
+                 * by a read request filter.
+                 */
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
+                              "h2_task_output(%s): write without response "
+                              "for %s %s %s",
+                              output->env->id, output->env->method, 
+                              output->env->authority, output->env->path);
+                f->c->aborted = 1;
+            }
+            if (output->env->io) {
+                apr_thread_cond_broadcast(output->env->io);
+            }
+            return APR_ECONNABORTED;
+        }
+        
+        return h2_mplx_out_open(output->env->mplx, output->env->stream_id, 
+                                response, f, bb, output->env->io);
+    }
+    return APR_EOF;
+}
+
+void h2_task_output_close(h2_task_output *output)
+{
+    open_if_needed(output, NULL, NULL);
+    if (output->state != H2_TASK_OUT_DONE) {
+        h2_mplx_out_close(output->env->mplx, output->env->stream_id);
+        output->state = H2_TASK_OUT_DONE;
+    }
+}
+
+int h2_task_output_has_started(h2_task_output *output)
+{
+    return output->state >= H2_TASK_OUT_STARTED;
+}
+
+/* Bring the data from the brigade (which represents the result of the
+ * request_rec out filter chain) into the h2_mplx for further sending
+ * on the master connection. 
+ */
+apr_status_t h2_task_output_write(h2_task_output *output,
+                                  ap_filter_t* f, apr_bucket_brigade* bb)
+{
+    if (APR_BRIGADE_EMPTY(bb)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                      "h2_task_output(%s): empty write", output->env->id);
+        return APR_SUCCESS;
+    }
+    
+    apr_status_t status = open_if_needed(output, f, bb);
+    if (status != APR_EOF) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+                      "h2_task_output(%s): opened and passed brigade", 
+                      output->env->id);
+        return status;
+    }
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                  "h2_task_output(%s): write brigade", output->env->id);
+    return h2_mplx_out_write(output->env->mplx, output->env->stream_id, 
+                             f, bb, output->env->io);
+}
+
+void h2_task_output_die(h2_task_output *output, int status, request_rec *r)
+{
+    h2_from_h1_die(output->from_h1, status, r);
+}
diff --git a/modules/http2/mod_h2/h2_task_output.h b/modules/http2/mod_h2/h2_task_output.h
new file mode 100644 (file)
index 0000000..3bb4575
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_task_output__
+#define __mod_h2__h2_task_output__
+
+/* h2_task_output reads a HTTP/1 response from the brigade and applies
+ * them to a h2_output_converter. The brigade is setup as the output brigade
+ * for our pseudo httpd conn_rec that is handling a specific h2_task.
+ * 
+ */
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task_env;
+struct h2_from_h1;
+
+typedef enum {
+    H2_TASK_OUT_INIT,
+    H2_TASK_OUT_STARTED,
+    H2_TASK_OUT_DONE,
+} h2_task_output_state_t;
+
+typedef struct h2_task_output h2_task_output;
+
+struct h2_task_output {
+    struct h2_task_env *env;
+    h2_task_output_state_t state;
+    struct h2_from_h1 *from_h1;
+};
+
+h2_task_output *h2_task_output_create(struct h2_task_env *env, apr_pool_t *pool,
+                                      apr_bucket_alloc_t *bucket_alloc);
+
+void h2_task_output_destroy(h2_task_output *output);
+
+apr_status_t h2_task_output_write(h2_task_output *output,
+                                  ap_filter_t* filter,
+                                  apr_bucket_brigade* brigade);
+
+void h2_task_output_close(h2_task_output *output);
+
+int h2_task_output_has_started(h2_task_output *output);
+
+void h2_task_output_die(h2_task_output *output, int status, request_rec *r);
+
+#endif /* defined(__mod_h2__h2_task_output__) */
diff --git a/modules/http2/mod_h2/h2_task_queue.c b/modules/http2/mod_h2/h2_task_queue.c
new file mode 100644 (file)
index 0000000..a81cc10
--- /dev/null
@@ -0,0 +1,88 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#include <httpd.h>
+#include <http_core.h>
+
+#include "h2_task.h"
+#include "h2_task_queue.h"
+
+
+h2_task_queue *h2_tq_create(long id, apr_pool_t *pool)
+{
+    h2_task_queue *q = apr_pcalloc(pool, sizeof(h2_task_queue));
+    if (q) {
+        q->id = id;
+        APR_RING_ELEM_INIT(q, link);
+        APR_RING_INIT(&q->tasks, h2_task, link);
+    }
+    return q;
+}
+
+void h2_tq_destroy(h2_task_queue *q)
+{
+    while (!H2_TASK_LIST_EMPTY(&q->tasks)) {
+        h2_task *task = H2_TASK_LIST_FIRST(&q->tasks);
+        H2_TASK_REMOVE(task);
+    }
+}
+
+static int in_list(h2_task_queue *q, h2_task *task)
+{
+    h2_task *e;
+    for (e = H2_TASK_LIST_FIRST(&q->tasks); 
+         e != H2_TASK_LIST_SENTINEL(&q->tasks);
+         e = H2_TASK_NEXT(e)) {
+        if (e == task) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int h2_tq_empty(h2_task_queue *q)
+{
+    return H2_TASK_LIST_EMPTY(&q->tasks);
+}
+
+void h2_tq_append(h2_task_queue *q, struct h2_task *task)
+{
+    H2_TASK_LIST_INSERT_TAIL(&q->tasks, task);
+}
+
+apr_status_t h2_tq_remove(h2_task_queue *q, struct h2_task *task)
+{
+    if (in_list(q, task)) {
+        H2_TASK_REMOVE(task);
+        return APR_SUCCESS;
+    }
+    return APR_NOTFOUND;
+}
+
+h2_task *h2_tq_pop_first(h2_task_queue *q)
+{
+    if (!H2_TASK_LIST_EMPTY(&q->tasks)) {
+        h2_task *task = H2_TASK_LIST_FIRST(&q->tasks);
+        H2_TASK_REMOVE(task);
+        return task;
+    }
+    return NULL;
+}
+
+
+
diff --git a/modules/http2/mod_h2/h2_task_queue.h b/modules/http2/mod_h2/h2_task_queue.h
new file mode 100644 (file)
index 0000000..d93d74a
--- /dev/null
@@ -0,0 +1,148 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_task_queue__
+#define __mod_h2__h2_task_queue__
+
+struct h2_task;
+
+/**
+ * A simple ring of rings that keeps a list of h2_tasks and can
+ * be ringed itself, using the APR RING macros.
+ */
+typedef struct h2_task_queue h2_task_queue;
+
+struct h2_task_queue {
+    APR_RING_ENTRY(h2_task_queue) link;
+    APR_RING_HEAD(h2_tasks, h2_task) tasks;
+    long id;
+};
+
+/**
+ * Allocate a new queue from the pool and initialize.
+ * @param id the identifier of the queue
+ * @param pool the memory pool
+ */
+h2_task_queue *h2_tq_create(long id, apr_pool_t *pool);
+
+/**
+ * Release all queue tasks.
+ * @param q the queue to destroy
+ */
+void h2_tq_destroy(h2_task_queue *q);
+
+/**
+ * Return != 0 iff there are no tasks in the queue.
+ * @param q the queue to check
+ */
+int h2_tq_empty(h2_task_queue *q);
+
+/**
+ * Append the task to the end of the queue.
+ * @param q the queue to append the task to
+ * @param task the task to append
+  */
+void h2_tq_append(h2_task_queue *q, struct h2_task *task);
+
+/**
+ * Remove a task from the queue. Return APR_SUCCESS if the task
+ * was indeed queued, APR_NOTFOUND otherwise.
+ * @param q the queue to remove from
+ * @param task the task to remove
+ */
+apr_status_t h2_tq_remove(h2_task_queue *q, struct h2_task *task);
+
+/**
+ * Get the first task from the queue or NULL if the queue is empty. The
+ * task will be removed.
+ * @param q the queue to pop the first task from
+ */
+h2_task *h2_tq_pop_first(h2_task_queue *q);
+
+/*******************************************************************************
+ * Queue Manipulation.
+ ******************************************************************************/
+
+/**
+ * The magic pointer value that indicates the head of a h2_task_queue list
+ * @param  b The queue list
+ * @return The magic pointer value
+ */
+#define H2_TQ_LIST_SENTINEL(b) APR_RING_SENTINEL((b), h2_task_queue, link)
+
+/**
+ * Determine if the queue list is empty
+ * @param b The list to check
+ * @return true or false
+ */
+#define H2_TQ_LIST_EMPTY(b)    APR_RING_EMPTY((b), h2_task_queue, link)
+
+/**
+ * Return the first queue in a list
+ * @param b The list to query
+ * @return The first queue in the list
+ */
+#define H2_TQ_LIST_FIRST(b)    APR_RING_FIRST(b)
+
+/**
+ * Return the last queue in a list
+ * @param b The list to query
+ * @return The last queue int he list
+ */
+#define H2_TQ_LIST_LAST(b)     APR_RING_LAST(b)
+
+/**
+ * Insert a single queue at the front of a list
+ * @param b The list to add to
+ * @param e The queue to insert
+ */
+#define H2_TQ_LIST_INSERT_HEAD(b, e) do {                              \
+h2_task_queue *ap__b = (e);                                        \
+APR_RING_INSERT_HEAD((b), ap__b, h2_task_queue, link); \
+} while (0)
+
+/**
+ * Insert a single queue at the end of a list
+ * @param b The list to add to
+ * @param e The queue to insert
+ */
+#define H2_TQ_LIST_INSERT_TAIL(b, e) do {                              \
+h2_task_queue *ap__b = (e);                                    \
+APR_RING_INSERT_TAIL((b), ap__b, h2_task_queue, link); \
+} while (0)
+
+/**
+ * Get the next queue in the list
+ * @param e The current queue
+ * @return The next queue
+ */
+#define H2_TQ_NEXT(e)  APR_RING_NEXT((e), link)
+/**
+ * Get the previous queue in the list
+ * @param e The current queue
+ * @return The previous queue
+ */
+#define H2_TQ_PREV(e)  APR_RING_PREV((e), link)
+
+/**
+ * Remove a queue from its list
+ * @param e The queue to remove
+ */
+#define H2_TQ_REMOVE(e)        APR_RING_REMOVE((e), link)
+
+
+#define H2_TQ_EMPTY(e) H2_TASK_LIST_EMPTY(&(e)->tasks)
+
+#endif /* defined(__mod_h2__h2_task_queue__) */
diff --git a/modules/http2/mod_h2/h2_to_h1.c b/modules/http2/mod_h2/h2_to_h1.c
new file mode 100644 (file)
index 0000000..f8ac54c
--- /dev/null
@@ -0,0 +1,288 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+
+#include "h2_private.h"
+#include "h2_mplx.h"
+#include "h2_response.h"
+#include "h2_task.h"
+#include "h2_to_h1.h"
+#include "h2_util.h"
+
+
+h2_to_h1 *h2_to_h1_create(int stream_id, apr_pool_t *pool, 
+                          apr_bucket_alloc_t *bucket_alloc, 
+                          const char *method, const char *path,
+                          const char *authority, struct h2_mplx *m)
+{
+    if (!method) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
+                      "h2_to_h1: header start but :method missing");
+        return NULL;
+    }
+    if (!path) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c,
+                      "h2_to_h1: header start but :path missing");
+        return NULL;
+    }
+    
+    h2_to_h1 *to_h1 = apr_pcalloc(pool, sizeof(h2_to_h1));
+    if (to_h1) {
+        to_h1->stream_id = stream_id;
+        to_h1->pool = pool;
+        to_h1->method = method;
+        to_h1->path = path;
+        to_h1->authority = authority;
+        to_h1->m = m;
+        to_h1->headers = apr_table_make(to_h1->pool, 10);
+        to_h1->bb = apr_brigade_create(pool, bucket_alloc);
+        to_h1->chunked = 0; /* until we see a content-type and no length */
+        to_h1->content_len = -1;
+    }
+    return to_h1;
+}
+
+void h2_to_h1_destroy(h2_to_h1 *to_h1)
+{
+    to_h1->bb = NULL;
+}
+
+apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1,
+                                 const char *name, size_t nlen,
+                                 const char *value, size_t vlen)
+{
+    if (H2_HD_MATCH_LIT("transfer-encoding", name, nlen)) {
+        if (!apr_strnatcasecmp("chunked", value)) {
+            /* This should never arrive here in a HTTP/2 request */
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_BADARG, to_h1->m->c,
+                          "h2_to_h1: 'transfer-encoding: chunked' received");
+            return APR_BADARG;
+        }
+    }
+    else if (H2_HD_MATCH_LIT("content-length", name, nlen)) {
+        char *end;
+        to_h1->content_len = apr_strtoi64(value, &end, 10);
+        if (value == end) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, to_h1->m->c,
+                          "h2_request(%d): content-length value not parsed: %s",
+                          to_h1->stream_id, value);
+            return APR_EINVAL;
+        }
+        to_h1->remain_len = to_h1->content_len;
+        to_h1->chunked = 0;
+    }
+    else if (H2_HD_MATCH_LIT("content-type", name, nlen)) {
+        /* If we see a content-type and have no length (yet),
+         * we need to chunk. */
+        to_h1->chunked = (to_h1->content_len == -1);
+    }
+    else if ((to_h1->seen_host && H2_HD_MATCH_LIT("host", name, nlen))
+             || H2_HD_MATCH_LIT("expect", name, nlen)
+             || H2_HD_MATCH_LIT("upgrade", name, nlen)
+             || H2_HD_MATCH_LIT("connection", name, nlen)
+             || H2_HD_MATCH_LIT("proxy-connection", name, nlen)
+             || H2_HD_MATCH_LIT("keep-alive", name, nlen)
+             || H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
+        // ignore these.
+        return APR_SUCCESS;
+    }
+    else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
+        const char *existing = apr_table_get(to_h1->headers, "cookie");
+        if (existing) {
+            /* Cookie headers come separately in HTTP/2, but need
+             * to be merged by "; " (instead of default ", ")
+             */
+            char *hvalue = apr_pstrndup(to_h1->pool, value, vlen);
+            char *nval = apr_psprintf(to_h1->pool, "%s; %s", existing, hvalue);
+            apr_table_setn(to_h1->headers, "Cookie", nval);
+            return APR_SUCCESS;
+        }
+    }
+    else if (H2_HD_MATCH_LIT("host", name, nlen)) {
+        to_h1->seen_host = 1;
+    }
+    
+    char *hname = apr_pstrndup(to_h1->pool, name, nlen);
+    char *hvalue = apr_pstrndup(to_h1->pool, value, vlen);
+    h2_util_camel_case_header(hname, nlen);
+    apr_table_mergen(to_h1->headers, hname, hvalue);
+    
+    return APR_SUCCESS;
+}
+
+static int set_header(void *ctx, const char *key, const char *value)
+{
+    h2_to_h1 *to_h1 = (h2_to_h1*)ctx;
+    h2_to_h1_add_header(to_h1, key, strlen(key), value, strlen(value));
+    return 1;
+}
+
+apr_status_t h2_to_h1_add_headers(h2_to_h1 *to_h1, apr_table_t *headers)
+{
+    apr_table_do(set_header, to_h1, headers, NULL);
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_to_h1_end_headers(h2_to_h1 *to_h1, h2_task *task, int eos)
+{
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, to_h1->m->c,
+                  "h2_to_h1(%ld-%d): end headers", 
+                  to_h1->m->id, to_h1->stream_id);
+    
+    if (to_h1->eoh) {
+        return APR_EINVAL;
+    }
+    
+    if (!to_h1->seen_host) {
+        /* Need to add a "Host" header if not already there to
+         * make virtual hosts work correctly. */
+        if (!to_h1->authority) {
+            return APR_BADARG;
+        }
+        apr_table_set(to_h1->headers, "Host", to_h1->authority);
+    }
+
+    if (eos && to_h1->chunked) {
+        /* We had chunking figured out, but the EOS is already there.
+         * unmark chunking and set a definitive content-length.
+         */
+        to_h1->chunked = 0;
+        apr_table_setn(to_h1->headers, "Content-Length", "0");
+    }
+    else if (to_h1->chunked) {
+        /* We have not seen a content-length. We therefore must
+         * pass any request content in chunked form.
+         */
+        apr_table_mergen(to_h1->headers, "Transfer-Encoding", "chunked");
+    }
+    
+    h2_task_set_request(task, to_h1->method, to_h1->path, 
+                        to_h1->authority, to_h1->headers, eos);
+    to_h1->eoh = 1;
+    
+    if (eos) {
+        apr_status_t status = h2_to_h1_close(to_h1);
+        if (status != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, to_h1->m->c,
+                          "h2_to_h1(%ld-%d): end headers, eos=%d", 
+                          to_h1->m->id, to_h1->stream_id, eos);
+        }
+        return status;
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t flush(apr_bucket_brigade *bb, void *ctx) 
+{
+    (void)bb;
+    return h2_to_h1_flush((h2_to_h1*)ctx);
+}
+
+static apr_status_t h2_to_h1_add_data_raw(h2_to_h1 *to_h1,
+                                          const char *data, size_t len)
+{
+    apr_status_t status = APR_SUCCESS;
+
+    if (to_h1->eos || !to_h1->eoh) {
+        return APR_EINVAL;
+    }
+    
+    status = apr_brigade_write(to_h1->bb, flush, to_h1, data, len);
+    if (status == APR_SUCCESS) {
+        status = h2_to_h1_flush(to_h1);
+    }
+    return status;
+}
+
+
+apr_status_t h2_to_h1_add_data(h2_to_h1 *to_h1,
+                               const char *data, size_t len)
+{
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, to_h1->m->c,
+                  "h2_to_h1(%ld-%d): add %ld data bytes", 
+                  to_h1->m->id, to_h1->stream_id, (long)len);
+    
+    if (to_h1->chunked) {
+        /* if input may have a body and we have not seen any
+         * content-length header, we need to chunk the input data.
+         */
+        apr_status_t status = apr_brigade_printf(to_h1->bb, NULL, NULL,
+                                                 "%lx\r\n", (unsigned long)len);
+        if (status == APR_SUCCESS) {
+            status = h2_to_h1_add_data_raw(to_h1, data, len);
+            if (status == APR_SUCCESS) {
+                status = apr_brigade_puts(to_h1->bb, NULL, NULL, "\r\n");
+            }
+        }
+        return status;
+    }
+    else {
+        to_h1->remain_len -= len;
+        if (to_h1->remain_len < 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, to_h1->m->c,
+                          "h2_to_h1(%ld-%d): got %ld more content bytes than announced "
+                          "in content-length header: %ld", 
+                          to_h1->m->id, to_h1->stream_id, 
+                          (long)to_h1->content_len, -(long)to_h1->remain_len);
+        }
+        return h2_to_h1_add_data_raw(to_h1, data, len);
+    }
+}
+
+apr_status_t h2_to_h1_flush(h2_to_h1 *to_h1)
+{
+    apr_status_t status = APR_SUCCESS;
+    if (!APR_BRIGADE_EMPTY(to_h1->bb)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, to_h1->m->c,
+                      "h2_to_h1(%ld-%d): flush request bytes", 
+                      to_h1->m->id, to_h1->stream_id);
+        
+        status = h2_mplx_in_write(to_h1->m, to_h1->stream_id, to_h1->bb);
+        if (status != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, status, to_h1->m->c,
+                          "h2_request(%d): pushing request data",
+                          to_h1->stream_id);
+        }
+    }
+    return status;
+}
+
+apr_status_t h2_to_h1_close(h2_to_h1 *to_h1)
+{
+    apr_status_t status = APR_SUCCESS;
+    if (!to_h1->eos) {
+        if (to_h1->chunked) {
+            status = h2_to_h1_add_data_raw(to_h1, "0\r\n\r\n", 5);
+        }
+        to_h1->eos = 1;
+        status = h2_to_h1_flush(to_h1);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, to_h1->m->c,
+                      "h2_to_h1(%d): close", to_h1->stream_id);
+        
+        status = h2_mplx_in_close(to_h1->m, to_h1->stream_id);
+    }
+    return status;
+}
+
+
diff --git a/modules/http2/mod_h2/h2_to_h1.h b/modules/http2/mod_h2/h2_to_h1.h
new file mode 100644 (file)
index 0000000..275448f
--- /dev/null
@@ -0,0 +1,83 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_to_h1__
+#define __mod_h2__h2_to_h1__
+
+struct h2_mplx;
+struct h2_task;
+typedef struct h2_to_h1 h2_to_h1;
+
+struct h2_to_h1 {
+    int stream_id;
+    apr_pool_t *pool;
+    h2_mplx *m;
+
+    const char *method;
+    const char *path;
+    const char *authority;
+    
+    int chunked;
+    int eoh;
+    int eos;
+    int flushed;
+    int seen_host;
+    
+    apr_off_t content_len;
+    apr_off_t remain_len;
+    apr_table_t *headers;
+    apr_bucket_brigade *bb;
+};
+
+/* Create a converter from a HTTP/2 request to a serialzation in
+ * HTTP/1.1 format. The serialized data will be written onto the
+ * given h2_mplx instance.
+ */
+h2_to_h1 *h2_to_h1_create(int stream_id, apr_pool_t *pool, 
+                          apr_bucket_alloc_t *bucket_alloc, 
+                          const char *method, const char *path,
+                          const char *authority, struct h2_mplx *m);
+
+/* Destroy the converter and free resources. */
+void h2_to_h1_destroy(h2_to_h1 *to_h1);
+
+/* Add a header to the serialization. Only valid to call after start
+ * and before end_headers.
+ */
+apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1,
+                                 const char *name, size_t nlen,
+                                 const char *value, size_t vlen);
+
+apr_status_t h2_to_h1_add_headers(h2_to_h1 *to_h1, apr_table_t *headers);
+
+/** End the request headers.
+ */
+apr_status_t h2_to_h1_end_headers(h2_to_h1 *to_h1, 
+                                  struct h2_task *task, int eos);
+
+/* Add request body data.
+ */
+apr_status_t h2_to_h1_add_data(h2_to_h1 *to_h1,
+                               const char *data, size_t len);
+
+/* Flush the converted data onto the h2_mplx instance.
+ */
+apr_status_t h2_to_h1_flush(h2_to_h1 *to_h1);
+
+/* Close the request, flushed automatically.
+ */
+apr_status_t h2_to_h1_close(h2_to_h1 *to_h1);
+
+#endif /* defined(__mod_h2__h2_to_h1__) */
diff --git a/modules/http2/mod_h2/h2_upgrade.c b/modules/http2/mod_h2/h2_upgrade.c
new file mode 100644 (file)
index 0000000..88d94cb
--- /dev/null
@@ -0,0 +1,199 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_optional.h>
+#include <apr_optional_hooks.h>
+
+#include <ap_mpm.h>
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_connection.h>
+#include <http_log.h>
+#include <http_protocol.h>
+#include <http_request.h>
+
+#include "h2_private.h"
+#include "h2_conn.h"
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_h2.h"
+#include "h2_upgrade.h"
+#include "h2_util.h"
+
+static int h2_upgrade_request_handler(request_rec *r);
+static const char *h2_get_upgrade_proto(request_rec *r);
+static int h2_upgrade_to(request_rec *r, const char *proto);
+static int h2_upgrade_options(request_rec *r);
+
+void h2_upgrade_register_hooks(void)
+{
+    ap_hook_handler(h2_upgrade_request_handler, NULL, NULL, APR_HOOK_FIRST - 1);
+    ap_hook_map_to_storage(h2_upgrade_options, NULL, NULL, APR_HOOK_FIRST);
+}
+
+static int h2_upgrade_options(request_rec *r)
+{
+    if ((r->method_number == M_OPTIONS) && r->uri && (r->uri[0] == '*') &&
+        (r->uri[1] == '\0')) {
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+                      "h2c: request OPTIONS * seen");
+        return h2_upgrade_request_handler(r);
+    }
+    return DECLINED;
+}
+
+static int h2_upgrade_request_handler(request_rec *r)
+{
+    h2_ctx *ctx = h2_ctx_rget(r);
+    h2_config *cfg = h2_config_rget(r);
+    int enabled_for_request = h2_config_geti(cfg, H2_CONF_ENABLED);
+    
+    if (h2_ctx_is_task(ctx) || h2_ctx_is_active(ctx)) {
+        /* talking h2 already, either task for main conn */
+        if (!enabled_for_request) {
+            /* we have a request for a server (vhost) where h2 is
+             * not enabled. This happened over a connection on which
+             * we talk h2.
+             * Tell the client, she should open a new connection to that
+             * vhost to get fresh protocol negotiations.
+             */
+            r->status = 421;
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r->status, r,
+                          "421-ing h2 request to host %s", r->hostname);
+            return DONE;
+        }
+        return DECLINED;
+    }
+    
+    /* not talking h2 (yet) */
+    if (enabled_for_request) {
+        /* Check for the start of an h2c Upgrade dance. */
+        const char *proto = h2_get_upgrade_proto(r);
+        if (proto) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                          "seeing %s upgrade invitation", proto);
+            /* We do not handle upgradeable requests with a body.
+             * The reason being that we would need to read the body in full
+             * before we ca use HTTP2 frames on the wire.
+             * 
+             * This seems to be consensus among server implemntations and
+             * clients are advised to use an "OPTIONS *" before a POST.
+             */
+            const char *clen = apr_table_get(r->headers_in, "Content-Length");
+            if (clen && strcmp(clen, "0")) {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                              "upgrade with content-length: %s, declined", clen);
+                return DECLINED;
+            }
+            return h2_upgrade_to(r, proto);
+        }
+    }
+    
+    return DECLINED;
+}
+
+static const char *h2_get_upgrade_proto(request_rec *r)
+{
+    const char *proto, *conn;
+    const char *upgrade = apr_table_get(r->headers_in, "Upgrade");
+    
+    if (upgrade && *upgrade) {
+        
+        conn = apr_table_get(r->headers_in, "Connection");
+        if (h2_util_contains_token(r->pool, conn, "Upgrade")
+            && apr_table_get(r->headers_in, "HTTP2-Settings")) {
+            
+            /* HTTP/1 Upgrade: is just another mechanism to switch
+             * protocols on a connection, same as ALPN or NPN.
+             * Security desirability aside, the bit protocol spoken
+             * afterwards is the same. Why require different identifier?
+             *
+             * We allow the same tokens as in ALPN negotiation, plus the
+             * special 'c' variants that RFC 7540 defines. We just do not
+             * care about the transport here.
+             */
+            proto = h2_util_first_token_match(r->pool, upgrade, 
+                                              h2_alpn_protos, 
+                                              h2_alpn_protos_len);
+            if (!proto) {
+                proto = h2_util_first_token_match(r->pool, upgrade, 
+                                                  h2_upgrade_protos, 
+                                                  h2_upgrade_protos_len);
+            }
+            
+            if (proto) {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                              "suiteable upgrade detected: %s %s, "
+                              "Upgrade: %s", r->method, r->uri, upgrade);
+                return proto;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static int h2_upgrade_to(request_rec *r, const char *proto)
+{
+    conn_rec *c = r->connection;
+    h2_ctx *ctx = h2_ctx_rget(r);
+    
+    h2_ctx_pnego_set_done(ctx, proto);
+    
+    /* Let the client know what we are upgrading to. */
+    apr_table_clear(r->headers_out);
+    apr_table_setn(r->headers_out, "Upgrade", proto);
+    apr_table_setn(r->headers_out, "Connection", "Upgrade");
+    
+    r->status = HTTP_SWITCHING_PROTOCOLS;
+    r->status_line = ap_get_status_line(r->status);
+    ap_send_interim_response(r, 1);
+    
+    /* Make sure the core filter that parses http1 requests does
+     * not mess with our http2 frames. */
+    if (APLOGrtrace2(r)) {
+        ap_filter_t *filter = r->input_filters;
+        while (filter) {
+            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+                          "h2_conn(%ld), has request filter %s",
+                          r->connection->id, filter->frec->name);
+            filter = filter->next;
+        }
+    }
+    ap_remove_input_filter_byhandle(r->input_filters, "http_in");
+    ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout");
+
+    /* Ok, start an h2_conn on this one. */
+    apr_status_t status = h2_conn_rprocess(r);
+    if (status != DONE) {
+        /* Nothing really to do about this. */
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
+                      "session proessed, unexpected status");
+    }
+    
+    /* make sure httpd closes the connection after this */
+    c->keepalive = AP_CONN_CLOSE;
+    ap_lingering_close(c);
+    
+    if (c->sbh) {
+        ap_update_child_status_from_conn(c->sbh, SERVER_CLOSING, c);
+    }
+
+    return DONE;
+}
+
diff --git a/modules/http2/mod_h2/h2_upgrade.h b/modules/http2/mod_h2/h2_upgrade.h
new file mode 100644 (file)
index 0000000..ee2dbaa
--- /dev/null
@@ -0,0 +1,24 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_upgrade__
+#define __mod_h2__h2_upgrade__
+
+/* Specific function to HTTP/1 connection Upgradeds.
+ */
+void h2_upgrade_register_hooks(void);
+
+
+#endif /* defined(__mod_h2__h2_upgrade__) */
diff --git a/modules/http2/mod_h2/h2_util.c b/modules/http2/mod_h2/h2_util.c
new file mode 100644 (file)
index 0000000..1b08165
--- /dev/null
@@ -0,0 +1,703 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include <nghttp2/nghttp2.h>
+
+#include "h2_private.h"
+#include "h2_util.h"
+
+size_t h2_util_hex_dump(char *buffer, size_t maxlen,
+                        const char *data, size_t datalen)
+{
+    size_t offset = 0;
+    size_t maxoffset = (maxlen-4);
+    size_t i;
+    for (i = 0; i < datalen && offset < maxoffset; ++i) {
+        const char *sep = (i && i % 16 == 0)? "\n" : " ";
+        int n = apr_snprintf(buffer+offset, maxoffset-offset,
+                             "%2x%s", ((unsigned int)data[i]&0xff), sep);
+        offset += n;
+    }
+    strcpy(buffer+offset, (i<datalen)? "..." : "");
+    return strlen(buffer);
+}
+
+size_t h2_util_header_print(char *buffer, size_t maxlen,
+                            const char *name, size_t namelen,
+                            const char *value, size_t valuelen)
+{
+    size_t offset = 0;
+    size_t i;
+    for (i = 0; i < namelen && offset < maxlen; ++i, ++offset) {
+        buffer[offset] = name[i];
+    }
+    for (i = 0; i < 2 && offset < maxlen; ++i, ++offset) {
+        buffer[offset] = ": "[i];
+    }
+    for (i = 0; i < valuelen && offset < maxlen; ++i, ++offset) {
+        buffer[offset] = value[i];
+    }
+    buffer[offset] = '\0';
+    return offset;
+}
+
+
+char *h2_strlwr(char *s)
+{
+    for (char *p = s; *p; ++p) {
+        if (*p >= 'A' && *p <= 'Z') {
+            *p += 'a' - 'A';
+        }
+    }
+    return s;
+}
+
+void h2_util_camel_case_header(char *s, size_t len)
+{
+    size_t start = 1;
+    for (size_t i = 0; i < len; ++i) {
+        if (start) {
+            if (s[i] >= 'a' && s[i] <= 'z') {
+                s[i] -= 'a' - 'A';
+            }
+            
+            start = 0;
+        }
+        else if (s[i] == '-') {
+            start = 1;
+        }
+    }
+}
+
+static const int BASE64URL_TABLE[] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
+    58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,
+    7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+    25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+    37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1
+};
+
+apr_size_t h2_util_base64url_decode(unsigned char **decoded, const char *encoded, 
+                                    apr_pool_t *pool)
+{
+    const unsigned char *e = (const unsigned char *)encoded;
+    const unsigned char *p = e;
+    int n;
+    
+    while (*p && BASE64URL_TABLE[ *p ] == -1) {
+        ++p;
+    }
+    apr_size_t len = p - e;
+    apr_size_t mlen = (len/4)*4;
+    *decoded = apr_pcalloc(pool, len+1);
+    
+    apr_size_t i = 0;
+    unsigned char *d = *decoded;
+    for (; i < mlen; i += 4) {
+        n = ((BASE64URL_TABLE[ e[i+0] ] << 18) +
+             (BASE64URL_TABLE[ e[i+1] ] << 12) +
+             (BASE64URL_TABLE[ e[i+2] ] << 6) +
+             BASE64URL_TABLE[ e[i+3] ]);
+        *d++ = n >> 16;
+        *d++ = n >> 8 & 0xffu;
+        *d++ = n & 0xffu;
+    }
+    apr_size_t remain = len - mlen;
+    switch (remain) {
+        case 2:
+            n = ((BASE64URL_TABLE[ e[mlen+0] ] << 18) +
+                 (BASE64URL_TABLE[ e[mlen+1] ] << 12));
+            *d++ = n >> 16;
+            break;
+        case 3:
+            n = ((BASE64URL_TABLE[ e[mlen+0] ] << 18) +
+                 (BASE64URL_TABLE[ e[mlen+1] ] << 12) +
+                 (BASE64URL_TABLE[ e[mlen+2] ] << 6));
+            *d++ = n >> 16;
+            *d++ = n >> 8 & 0xffu;
+            break;
+        default: /* do nothing */
+            break;
+    }
+    return len;
+}
+
+int h2_util_contains_token(apr_pool_t *pool, const char *s, const char *token)
+{
+    if (s) {
+        if (!apr_strnatcasecmp(s, token)) {   /* the simple life */
+            return 1;
+        }
+        
+        for (char *c = ap_get_token(pool, &s, 0); c && *c;
+             c = *s? ap_get_token(pool, &s, 0) : NULL) {
+            if (!apr_strnatcasecmp(c, token)) { /* seeing the token? */
+                return 1;
+            }
+            while (*s++ == ';') {            /* skip parameters */
+                ap_get_token(pool, &s, 0);
+            }
+            if (*s++ != ',') {               /* need comma separation */
+                return 0;
+            }
+        }
+    }
+    return 0;
+}
+
+const char *h2_util_first_token_match(apr_pool_t *pool, const char *s, 
+                                      const char *tokens[], apr_size_t len)
+{
+    if (s && *s) {
+        for (char *c = ap_get_token(pool, &s, 0); c && *c;
+             c = *s? ap_get_token(pool, &s, 0) : NULL) {
+            for (apr_size_t i = 0; i < len; ++i) {
+                if (!apr_strnatcasecmp(c, tokens[i])) {
+                    return tokens[i];
+                }
+            }
+            while (*s++ == ';') {            /* skip parameters */
+                ap_get_token(pool, &s, 0);
+            }
+            if (*s++ != ',') {               /* need comma separation */
+                return 0;
+            }
+        }
+    }
+    return NULL;
+}
+
+/* DEEP_COPY==0 crashes under load. I think the setaside is fine, 
+ * however buckets moved to another thread will still be
+ * free'd against the old bucket_alloc. *And* if the old
+ * pool gets destroyed too early, the bucket disappears while
+ * still needed.
+ */
+static const int DEEP_COPY = 1;
+static const int FILE_MOVE = 0;
+
+static apr_status_t last_not_included(apr_bucket_brigade *bb, 
+                                      apr_size_t maxlen, int count_virtual,
+                                      apr_bucket **pend)
+{
+    apr_bucket *b;
+    apr_status_t status = APR_SUCCESS;
+    
+    if (maxlen > 0) {
+        /* Find the bucket, up to which we reach maxlen/mem bytes */
+        for (b = APR_BRIGADE_FIRST(bb); 
+             (b != APR_BRIGADE_SENTINEL(bb));
+             b = APR_BUCKET_NEXT(b)) {
+            
+            if (APR_BUCKET_IS_METADATA(b)) {
+                /* included */
+            }
+            else {
+                if (maxlen == 0) {
+                    *pend = b;
+                    return status;
+                }
+                
+                if (b->length == ((apr_size_t)-1)) {
+                    const char *ign;
+                    apr_size_t ilen;
+                    status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
+                    if (status != APR_SUCCESS) {
+                        return status;
+                    }
+                }
+                
+                if (!count_virtual && FILE_MOVE && APR_BUCKET_IS_FILE(b)) {
+                    /* this has no memory footprint really unless
+                     * it is read, disregard it in length count,
+                     * unless we count the virtual buckets */
+                }
+                else if (maxlen < b->length) {
+                    apr_bucket_split(b, maxlen);
+                    maxlen = 0;
+                }
+                else {
+                    maxlen -= b->length;
+                }
+            }
+        }
+    }
+    *pend = APR_BRIGADE_SENTINEL(bb);
+    return status;
+}
+
+#define LOG_LEVEL APLOG_TRACE2
+
+apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, 
+                          apr_size_t maxlen, int count_virtual, 
+                          apr_file_t **pfile, const char *msg)
+{
+    apr_status_t status = APR_SUCCESS;
+    
+    AP_DEBUG_ASSERT(to);
+    AP_DEBUG_ASSERT(from);
+    int same_alloc = (to->bucket_alloc == from->bucket_alloc);
+    
+    if (!APR_BRIGADE_EMPTY(from)) {
+        apr_bucket *b, *end;
+        
+        status = last_not_included(from, maxlen, 
+                                   (count_virtual || !FILE_MOVE), &end);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        
+        while (!APR_BRIGADE_EMPTY(from) && status == APR_SUCCESS) {
+            b = APR_BRIGADE_FIRST(from);
+            if (b == end) {
+                break;
+            }
+            
+            if (same_alloc || (b->list == to->bucket_alloc)) {
+                /* both brigades use the same bucket_alloc and auto-cleanups
+                 * have the same life time. It's therefore safe to just move
+                 * directly. */
+                APR_BUCKET_REMOVE(b);
+                APR_BRIGADE_INSERT_TAIL(to, b);
+                ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                              "h2_util_move: %s, passed bucket(same bucket_alloc) "
+                              "%ld-%ld, type=%s",
+                              msg, (long)b->start, (long)b->length, 
+                              APR_BUCKET_IS_METADATA(b)? 
+                              (APR_BUCKET_IS_EOS(b)? "EOS": 
+                               (APR_BUCKET_IS_FLUSH(b)? "FLUSH" : "META")) : 
+                              (APR_BUCKET_IS_FILE(b)? "FILE" : "DATA"));
+            }
+            else if (DEEP_COPY) {
+                /* we have not managed the magic of passing buckets from
+                 * one thread to another. Any attempts result in
+                 * cleanup of pools scrambling memory.
+                 */
+                if (APR_BUCKET_IS_METADATA(b)) {
+                    if (APR_BUCKET_IS_EOS(b)) {
+                        APR_BRIGADE_INSERT_TAIL(to, apr_bucket_eos_create(to->bucket_alloc));
+                        ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                                      "h2_util_move: %s, copied EOS bucket", msg);
+                    }
+                    else if (APR_BUCKET_IS_FLUSH(b)) {
+                        APR_BRIGADE_INSERT_TAIL(to, apr_bucket_flush_create(to->bucket_alloc));
+                        ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                                      "h2_util_move: %s, copied FLUSH bucket", msg);
+                    }
+                    else {
+                        /* ignore */
+                    }
+                }
+                else if (pfile && FILE_MOVE && APR_BUCKET_IS_FILE(b)) {
+                    /* We do not want to read files when passing buckets, if
+                     * we can avoid it. However, what we've come up so far
+                     * is not working corrently, resulting either in crashes or
+                     * too many open file descriptors.
+                     */
+                    apr_bucket_file *f = (apr_bucket_file *)b->data;
+                    apr_file_t *fd = f->fd;
+                    int setaside = (f->readpool != to->p);
+                    ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                                  "h2_util_move: %s, moving FILE bucket %ld-%ld "
+                                  "from=%lx(p=%lx) to=%lx(p=%lx), setaside=%d",
+                                  msg, (long)b->start, (long)b->length, 
+                                  (long)from, (long)from->p, 
+                                  (long)to, (long)to->p, setaside);
+                    if (setaside) {
+                        status = apr_file_setaside(&fd, fd, to->p);
+                        *pfile = fd;
+                        if (status != APR_SUCCESS) {
+                            ap_log_perror(APLOG_MARK, APLOG_ERR, status, to->p,
+                                          "h2_util: %s, setaside FILE", msg);
+                            return status;
+                        }
+                    }
+                    apr_brigade_insert_file(to, fd, b->start, b->length, 
+                                            to->p);
+                }
+                else {
+                    const char *data;
+                    apr_size_t len;
+                    status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+                    if (status == APR_SUCCESS && len > 0) {
+                        status = apr_brigade_write(to, NULL, NULL, data, len);
+                        ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                                      "h2_util_move: %s, copied bucket %ld-%ld "
+                                      "from=%lx(p=%lx) to=%lx(p=%lx)",
+                                      msg, (long)b->start, (long)b->length, 
+                                      (long)from, (long)from->p, 
+                                      (long)to, (long)to->p);
+                    }
+                }
+                apr_bucket_delete(b);
+            }
+            else {
+                apr_bucket_setaside(b, to->p);
+                APR_BUCKET_REMOVE(b);
+                APR_BRIGADE_INSERT_TAIL(to, b);
+                ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                              "h2_util_move: %s, passed setaside bucket %ld-%ld "
+                              "from=%lx(p=%lx) to=%lx(p=%lx)",
+                              msg, (long)b->start, (long)b->length, 
+                              (long)from, (long)from->p, 
+                              (long)to, (long)to->p);
+            }
+        }
+    }
+    
+    return status;
+}
+
+apr_status_t h2_util_pass(apr_bucket_brigade *to, apr_bucket_brigade *from, 
+                          apr_size_t maxlen, int count_virtual, 
+                          const char *msg)
+{
+    apr_status_t status = APR_SUCCESS;
+    
+    AP_DEBUG_ASSERT(to);
+    AP_DEBUG_ASSERT(from);
+    
+    if (!APR_BRIGADE_EMPTY(from)) {
+        apr_bucket *b, *end;
+        
+        status = last_not_included(from, maxlen, count_virtual, &end);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+
+        while (!APR_BRIGADE_EMPTY(from) && status == APR_SUCCESS) {
+            b = APR_BRIGADE_FIRST(from);
+            if (b == end) {
+                break;
+            }
+            
+            APR_BUCKET_REMOVE(b);
+            if (APR_BUCKET_IS_METADATA(b)) {
+                if (!APR_BUCKET_IS_EOS(b) && !APR_BUCKET_IS_FLUSH(b)) {
+                    apr_bucket_delete(b);
+                    continue;
+                }
+            }
+            else if (b->length == 0) {
+                apr_bucket_delete(b);
+                continue;
+            }
+            
+            APR_BRIGADE_INSERT_TAIL(to, b);
+            ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                          "h2_util_pass: %s, passed bucket %ld-%ld, type=%s",
+                          msg, (long)b->start, (long)b->length, 
+                          APR_BUCKET_IS_METADATA(b)? 
+                          (APR_BUCKET_IS_EOS(b)? "EOS": 
+                           (APR_BUCKET_IS_FLUSH(b)? "FLUSH" : "META")) : 
+                          (APR_BUCKET_IS_FILE(b)? "FILE" : "DATA"));
+        }
+    }
+    
+    return status;
+}
+
+apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from, 
+                          apr_size_t maxlen, const char *msg)
+{
+    apr_status_t status = APR_SUCCESS;
+    
+    AP_DEBUG_ASSERT(to);
+    AP_DEBUG_ASSERT(from);
+    int same_alloc = (to->bucket_alloc == from->bucket_alloc);
+
+    if (!APR_BRIGADE_EMPTY(from)) {
+        apr_bucket *b, *end, *cpy;
+        
+        status = last_not_included(from, maxlen, 1, &end);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+
+        for (b = APR_BRIGADE_FIRST(from);
+             b != APR_BRIGADE_SENTINEL(from) && b != end;
+             b = APR_BUCKET_NEXT(b))
+        {
+            if (same_alloc) {
+                status = apr_bucket_copy(b, &cpy);
+                if (status != APR_SUCCESS) {
+                    break;
+                }
+                APR_BRIGADE_INSERT_TAIL(to, cpy);
+            }
+            else {
+                if (APR_BUCKET_IS_METADATA(b)) {
+                    if (APR_BUCKET_IS_EOS(b)) {
+                        APR_BRIGADE_INSERT_TAIL(to, apr_bucket_eos_create(to->bucket_alloc));
+                        ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                                      "h2_util_copy: %s, copied EOS bucket", msg);
+                    }
+                    else if (APR_BUCKET_IS_FLUSH(b)) {
+                        APR_BRIGADE_INSERT_TAIL(to, apr_bucket_flush_create(to->bucket_alloc));
+                        ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                                      "h2_util_copy: %s, copied FLUSH bucket", msg);
+                    }
+                    else {
+                        /* ignore */
+                    }
+                }
+                else {
+                    const char *data;
+                    apr_size_t len;
+                    status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+                    if (status == APR_SUCCESS && len > 0) {
+                        status = apr_brigade_write(to, NULL, NULL, data, len);
+                        ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p,
+                                      "h2_util_copy: %s, copied bucket %ld-%ld "
+                                      "from=%lx(p=%lx) to=%lx(p=%lx)",
+                                      msg, (long)b->start, (long)b->length, 
+                                      (long)from, (long)from->p, 
+                                      (long)to, (long)to->p);
+                    }
+                }
+            }
+        }
+    }
+    return status;
+}
+
+int h2_util_has_flush_or_eos(apr_bucket_brigade *bb) {
+    apr_bucket *b;
+    for (b = APR_BRIGADE_FIRST(bb);
+         b != APR_BRIGADE_SENTINEL(bb);
+         b = APR_BUCKET_NEXT(b))
+    {
+        if (APR_BUCKET_IS_EOS(b) || APR_BUCKET_IS_FLUSH(b)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int h2_util_has_eos(apr_bucket_brigade *bb, apr_size_t len)
+{
+    apr_bucket *b, *end;
+    
+    apr_status_t status = last_not_included(bb, len, 1, &end);
+    if (status != APR_SUCCESS) {
+        return status;
+    }
+    
+    for (b = APR_BRIGADE_FIRST(bb);
+         b != APR_BRIGADE_SENTINEL(bb) && b != end;
+         b = APR_BUCKET_NEXT(b))
+    {
+        if (APR_BUCKET_IS_EOS(b)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int h2_util_bb_has_data(apr_bucket_brigade *bb)
+{
+    apr_bucket *b;
+    for (b = APR_BRIGADE_FIRST(bb);
+         b != APR_BRIGADE_SENTINEL(bb);
+         b = APR_BUCKET_NEXT(b))
+    {
+        if (!APR_BUCKET_IS_METADATA(b)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb)
+{
+    apr_bucket *b;
+    for (b = APR_BRIGADE_FIRST(bb);
+         b != APR_BRIGADE_SENTINEL(bb);
+         b = APR_BUCKET_NEXT(b))
+    {
+        if (APR_BUCKET_IS_METADATA(b)) {
+            if (APR_BUCKET_IS_EOS(b)) {
+                return 1;
+            }
+        }
+        else {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, 
+                              apr_size_t *plen, int *peos)
+{
+    apr_status_t status;
+    /* test read to determine available length */
+    apr_off_t blen = 0;
+    status = apr_brigade_length(bb, 0, &blen);
+    if (blen < (apr_off_t)*plen) {
+        *plen = blen;
+    }
+    *peos = h2_util_has_eos(bb, *plen);
+    return status;
+}
+
+apr_status_t h2_util_bb_read(apr_bucket_brigade *bb, char *buffer, 
+                             apr_size_t *plen, int *peos)
+{
+    apr_status_t status = APR_SUCCESS;
+    apr_size_t avail = *plen;
+    apr_size_t written = 0;
+
+    /* Copy data in our brigade into the buffer until it is filled or
+     * we encounter an EOS.
+     */
+    *peos = 0;
+    while ((status == APR_SUCCESS) && !APR_BRIGADE_EMPTY(bb)) {
+        
+        apr_bucket *b = APR_BRIGADE_FIRST(bb);
+        if (APR_BUCKET_IS_METADATA(b)) {
+            if (APR_BUCKET_IS_EOS(b)) {
+                *peos = 1;
+            }
+            else {
+                /* ignore */
+            }
+        }
+        else if (avail <= 0) {
+            break;
+        } 
+        else {
+            const char *data;
+            apr_size_t data_len;
+            
+            if (b->length != ((apr_size_t)-1) && b->length > avail) {
+                apr_bucket_split(b, avail);
+            }
+            status = apr_bucket_read(b, &data, &data_len, 
+                                     APR_NONBLOCK_READ);
+            if (status == APR_SUCCESS && data_len > 0) {
+                if (data_len > avail) {
+                    apr_bucket_split(b, avail);
+                    data_len = avail;
+                }
+                memcpy(buffer, data, data_len);
+                avail -= data_len;
+                buffer += data_len;
+                written += data_len;
+            }
+        }
+        apr_bucket_delete(b);
+    }
+    
+    *plen = written;
+    if (status == APR_SUCCESS && !*peos && !*plen) {
+        return APR_EAGAIN;
+    }
+    return status;
+}
+
+apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, 
+                              h2_util_pass_cb *cb, void *ctx, 
+                              apr_size_t *plen, int *peos)
+{
+    apr_status_t status = APR_SUCCESS;
+    apr_size_t avail = *plen;
+    apr_size_t written = 0;
+    apr_bucket *next, *b = APR_BRIGADE_FIRST(bb);
+    int consume = (cb != NULL);
+    
+    /* Pass data in our brigade through the callback until the length
+     * is satisfied or we encounter an EOS.
+     */
+    *peos = 0;
+    for (b = APR_BRIGADE_FIRST(bb);
+         (status == APR_SUCCESS) && (b != APR_BRIGADE_SENTINEL(bb));
+         b = next) {
+        
+        if (APR_BUCKET_IS_METADATA(b)) {
+            if (APR_BUCKET_IS_EOS(b)) {
+                *peos = 1;
+            }
+            else {
+                /* ignore */
+            }
+        }
+        else if (avail <= 0) {
+            break;
+        } 
+        else {
+            const char *data = NULL;
+            apr_size_t data_len;
+            
+            if (b->length != ((apr_size_t)-1)) {
+                status = apr_bucket_read(b, &data, &data_len, 
+                                         APR_NONBLOCK_READ);
+            }
+            else {
+                data_len = b->length;
+            }
+            
+            if (data_len > avail) {
+                apr_bucket_split(b, avail);
+                data_len = avail;
+            }
+            
+            if (consume) {
+                if (!data) {
+                    status = apr_bucket_read(b, &data, &data_len, 
+                                             APR_NONBLOCK_READ);
+                }
+                if (status == APR_SUCCESS) {
+                    status = cb(ctx, data, data_len);
+                }
+            }
+            else {
+                data_len = b->length;
+            }
+            avail -= data_len;
+            written += data_len;
+        }
+        
+        next = APR_BUCKET_NEXT(b);
+        if (consume) {
+            apr_bucket_delete(b);
+        }
+    }
+    
+    *plen = written;
+    if (status == APR_SUCCESS && !*peos && !*plen) {
+        return APR_EAGAIN;
+    }
+    return status;
+}
+
diff --git a/modules/http2/mod_h2/h2_util.h b/modules/http2/mod_h2/h2_util.h
new file mode 100644 (file)
index 0000000..a1438cc
--- /dev/null
@@ -0,0 +1,140 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_util__
+#define __mod_h2__h2_util__
+
+struct nghttp2_frame;
+
+size_t h2_util_hex_dump(char *buffer, size_t maxlen,
+                        const char *data, size_t datalen);
+
+size_t h2_util_header_print(char *buffer, size_t maxlen,
+                            const char *name, size_t namelen,
+                            const char *value, size_t valuelen);
+
+char *h2_strlwr(char *s);
+
+void h2_util_camel_case_header(char *s, size_t len);
+
+/**
+ * Return != 0 iff the string s contains the token, as specified in
+ * HTTP header syntax, rfc7230.
+ */
+int h2_util_contains_token(apr_pool_t *pool, const char *s, const char *token);
+
+const char *h2_util_first_token_match(apr_pool_t *pool, const char *s, 
+                                      const char *tokens[], apr_size_t len);
+
+/**
+ * I always wanted to write my own base64url decoder...not. See 
+ * https://tools.ietf.org/html/rfc4648#section-5 for description.
+ */
+apr_size_t h2_util_base64url_decode(unsigned char **decoded, 
+                                    const char *encoded, 
+                                    apr_pool_t *pool);
+
+#define H2_HD_MATCH_LIT(l, name, nlen)  \
+    ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+
+#define H2_HD_MATCH_LIT_CS(l, name)  \
+    ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+
+#define H2_CREATE_NV_LIT_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME;      \
+                                             nv->namelen = sizeof(NAME) - 1;  \
+                                             nv->value = (uint8_t *)VALUE;    \
+                                             nv->valuelen = strlen(VALUE)
+
+#define H2_CREATE_NV_CS_LIT(nv, NAME, VALUE) nv->name = (uint8_t *)NAME;      \
+                                             nv->namelen = strlen(NAME);      \
+                                             nv->value = (uint8_t *)VALUE;    \
+                                             nv->valuelen = sizeof(VALUE) - 1
+
+#define H2_CREATE_NV_CS_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME;       \
+                                            nv->namelen = strlen(NAME);       \
+                                            nv->value = (uint8_t *)VALUE;     \
+                                            nv->valuelen = strlen(VALUE)
+
+/**
+ * Moves data from one brigade into another. If maxlen > 0, it only
+ * moves up to maxlen bytes into the target brigade, making bucket splits
+ * if needed.
+ * @param to the brigade to move the data to
+ * @param from the brigade to get the data from
+ * @param maxlen of bytes to move, 0 for all
+ * @param count_virtual if virtual buckets like FILE do count against maxlen
+ * @param msg message for use in logging
+ */
+apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, 
+                          apr_size_t maxlen, int count_virtual, 
+                          apr_file_t **pfile, const char *msg);
+
+/**
+ * Copies buckets from one brigade into another. If maxlen > 0, it only
+ * copies up to maxlen bytes into the target brigade, making bucket splits
+ * if needed.
+ * @param to the brigade to copy the data to
+ * @param from the brigade to get the data from
+ * @param maxlen of bytes to copy, 0 for all
+ * @param msg message for use in logging
+ */
+apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from, 
+                          apr_size_t maxlen, const char *msg);
+
+/**
+ * Pass the buckets from one brigade into another, without any setaside. 
+ * Only recommended if both brigades use the same bucket alloc or if
+ * you really know what you are doing.
+ * @param to the brigade to pass the buckets to
+ * @param from the brigade to get the buckets from
+ * @param maxlen of bucket bytes to copy, 0 for all
+ * @param count_virtual if virtual buckets like FILE do count against maxlen
+ * @param msg message for use in logging
+ */
+apr_status_t h2_util_pass(apr_bucket_brigade *to, apr_bucket_brigade *from, 
+                          apr_size_t maxlen, int count_virtual, 
+                          const char *msg);
+
+/**
+ * Return != 0 iff there is a FLUSH or EOS bucket in the brigade.
+ * @param bb the brigade to check on
+ * @return != 0 iff brigade holds FLUSH or EOS bucket (or both)
+ */
+int h2_util_has_flush_or_eos(apr_bucket_brigade *bb);
+int h2_util_has_eos(apr_bucket_brigade *bb, apr_size_t len);
+int h2_util_bb_has_data(apr_bucket_brigade *bb);
+int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb);
+
+/**
+ * Check how many bytes of the desired amount are available and if the
+ * end of stream is reached by that amount.
+ * @param bb the brigade to check
+ * @param plen the desired length and, on return, the available length
+ * @param on return, if eos has been reached
+ */
+apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, 
+                              apr_size_t *plen, int *peos);
+
+apr_status_t h2_util_bb_read(apr_bucket_brigade *bb, char *buffer, 
+                             apr_size_t *plen, int *peos);
+
+typedef apr_status_t h2_util_pass_cb(void *ctx, 
+                                       const char *data, apr_size_t len);
+
+apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, 
+                              h2_util_pass_cb *cb, void *ctx, 
+                              apr_size_t *plen, int *peos);
+
+#endif /* defined(__mod_h2__h2_util__) */
diff --git a/modules/http2/mod_h2/h2_version.h.in b/modules/http2/mod_h2/h2_version.h.in
new file mode 100644 (file)
index 0000000..049a13f
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#ifndef mod_h2_h2_version_h
+#define mod_h2_h2_version_h
+
+/**
+ * @macro
+ * Version number of the h2 module as c string
+ */
+#define MOD_H2_VERSION "@PACKAGE_VERSION@"
+
+/**
+ * @macro
+ * Numerical representation of the version number of the h2 module
+ * release. This is a 24 bit number with 8 bits for major number, 8 bits
+ * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
+ */
+#define MOD_H2_VERSION_NUM @PACKAGE_VERSION_NUM@
+
+
+/**
+ * @macro
+ * != 0 iff the nghttp2 library supports data callbacks.
+ * Disabled for now since it lowers performance.
+ */
+#define NGHTTP2_HAS_DATA_CBXXX @NGHTTP2_HAS_DATA_CB@
+#define NGHTTP2_HAS_DATA_CB 0
+
+#endif /* mod_h2_h2_version_h */
diff --git a/modules/http2/mod_h2/h2_worker.c b/modules/http2/mod_h2/h2_worker.c
new file mode 100644 (file)
index 0000000..2e2a1d4
--- /dev/null
@@ -0,0 +1,168 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_thread_cond.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_mplx.h"
+#include "h2_task.h"
+#include "h2_worker.h"
+
+static void *execute(apr_thread_t *thread, void *wctx)
+{
+    h2_worker *worker = (h2_worker *)wctx;
+    apr_status_t status = APR_SUCCESS;
+    h2_mplx *m;
+    (void)thread;
+    
+    /* Furthermore, other code might want to see the socket for
+     * this connection. Allocate one without further function...
+     */
+    status = apr_socket_create(&worker->socket,
+                               APR_INET, SOCK_STREAM,
+                               APR_PROTO_TCP, worker->pool);
+    if (status != APR_SUCCESS) {
+        ap_log_perror(APLOG_MARK, APLOG_ERR, status, worker->pool,
+                      "h2_worker(%d): alloc socket", worker->id);
+        worker->worker_done(worker, worker->ctx);
+        return NULL;
+    }
+    
+    worker->task = NULL;
+    m = NULL;
+    while (!worker->aborted) {
+        status = worker->get_next(worker, &m, &worker->task, worker->ctx);
+        
+        if (worker->task) {            
+            h2_task_do(worker->task, worker);
+            worker->task = NULL;
+            apr_thread_cond_signal(h2_worker_get_cond(worker));
+        }
+    }
+
+    status = worker->get_next(worker, &m, NULL, worker->ctx);
+    m = NULL;
+    
+    if (worker->socket) {
+        apr_socket_close(worker->socket);
+        worker->socket = NULL;
+    }
+    
+    worker->worker_done(worker, worker->ctx);
+    return NULL;
+}
+
+h2_worker *h2_worker_create(int id,
+                            apr_pool_t *parent_pool,
+                            apr_threadattr_t *attr,
+                            h2_worker_mplx_next_fn *get_next,
+                            h2_worker_done_fn *worker_done,
+                            void *ctx)
+{
+    apr_allocator_t *allocator = NULL;
+    apr_pool_t *pool = NULL;
+    
+    apr_status_t status = apr_allocator_create(&allocator);
+    if (status != APR_SUCCESS) {
+        return NULL;
+    }
+    
+    status = apr_pool_create_ex(&pool, parent_pool, NULL, allocator);
+    if (status != APR_SUCCESS) {
+        return NULL;
+    }
+    apr_allocator_owner_set(allocator, pool);
+
+    h2_worker *w = apr_pcalloc(pool, sizeof(h2_worker));
+    if (w) {
+        APR_RING_ELEM_INIT(w, link);
+        
+        w->id = id;
+        w->pool = pool;
+        w->bucket_alloc = apr_bucket_alloc_create(pool);
+
+        w->get_next = get_next;
+        w->worker_done = worker_done;
+        w->ctx = ctx;
+        
+        status = apr_thread_cond_create(&w->io, w->pool);
+        if (status != APR_SUCCESS) {
+            return NULL;
+        }
+        
+        apr_thread_create(&w->thread, attr, execute, w, pool);
+    }
+    return w;
+}
+
+apr_status_t h2_worker_destroy(h2_worker *worker)
+{
+    if (worker->io) {
+        apr_thread_cond_destroy(worker->io);
+        worker->io = NULL;
+    }
+    if (worker->pool) {
+        apr_pool_destroy(worker->pool);
+        /* worker is gone */
+    }
+    return APR_SUCCESS;
+}
+
+int h2_worker_get_id(h2_worker *worker)
+{
+    return worker->id;
+}
+
+void h2_worker_abort(h2_worker *worker)
+{
+    worker->aborted = 1;
+}
+
+int h2_worker_is_aborted(h2_worker *worker)
+{
+    return worker->aborted;
+}
+
+apr_thread_t *h2_worker_get_thread(h2_worker *worker)
+{
+    return worker->thread;
+}
+
+apr_thread_cond_t *h2_worker_get_cond(h2_worker *worker)
+{
+    return worker->io;
+}
+
+apr_socket_t *h2_worker_get_socket(h2_worker *worker)
+{
+    return worker->socket;
+}
+
+apr_pool_t *h2_worker_get_pool(h2_worker *worker)
+{
+    return worker->pool;
+}
+
+apr_bucket_alloc_t *h2_worker_get_bucket_alloc(h2_worker *worker)
+{
+    return worker->bucket_alloc;
+}
+
diff --git a/modules/http2/mod_h2/h2_worker.h b/modules/http2/mod_h2/h2_worker.h
new file mode 100644 (file)
index 0000000..9c69e6b
--- /dev/null
@@ -0,0 +1,155 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_worker__
+#define __mod_h2__h2_worker__
+
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task;
+
+/* h2_worker is a basically a apr_thread_t that reads fromt he h2_workers
+ * task queue and runs h2_tasks it is given.
+ */
+typedef struct h2_worker h2_worker;
+
+/* Invoked when the worker wants a new task to process. Will block
+ * until a h2_mplx becomes available or the worker itself
+ * gets aborted (idle timeout, for example). */
+typedef apr_status_t h2_worker_mplx_next_fn(h2_worker *worker,
+                                            struct h2_mplx **pm,
+                                            struct h2_task **ptask,
+                                            void *ctx);
+
+/* Invoked just before the worker thread exits. */
+typedef void h2_worker_done_fn(h2_worker *worker, void *ctx);
+
+
+struct h2_worker {
+    /** Links to the rest of the workers */
+    APR_RING_ENTRY(h2_worker) link;
+    
+    int id;
+    apr_thread_t *thread;
+    apr_pool_t *pool;
+    apr_bucket_alloc_t *bucket_alloc;
+    struct apr_thread_cond_t *io;
+    apr_socket_t *socket;
+    
+    h2_worker_mplx_next_fn *get_next;
+    h2_worker_done_fn *worker_done;
+    void *ctx;
+    
+    int aborted;
+    struct h2_task *task;
+};
+
+/**
+ * The magic pointer value that indicates the head of a h2_worker list
+ * @param  b The worker list
+ * @return The magic pointer value
+ */
+#define H2_WORKER_LIST_SENTINEL(b)     APR_RING_SENTINEL((b), h2_worker, link)
+
+/**
+ * Determine if the worker list is empty
+ * @param b The list to check
+ * @return true or false
+ */
+#define H2_WORKER_LIST_EMPTY(b)        APR_RING_EMPTY((b), h2_worker, link)
+
+/**
+ * Return the first worker in a list
+ * @param b The list to query
+ * @return The first worker in the list
+ */
+#define H2_WORKER_LIST_FIRST(b)        APR_RING_FIRST(b)
+
+/**
+ * Return the last worker in a list
+ * @param b The list to query
+ * @return The last worker int he list
+ */
+#define H2_WORKER_LIST_LAST(b) APR_RING_LAST(b)
+
+/**
+ * Insert a single worker at the front of a list
+ * @param b The list to add to
+ * @param e The worker to insert
+ */
+#define H2_WORKER_LIST_INSERT_HEAD(b, e) do {                          \
+       h2_worker *ap__b = (e);                                        \
+       APR_RING_INSERT_HEAD((b), ap__b, h2_worker, link);      \
+    } while (0)
+
+/**
+ * Insert a single worker at the end of a list
+ * @param b The list to add to
+ * @param e The worker to insert
+ */
+#define H2_WORKER_LIST_INSERT_TAIL(b, e) do {                          \
+       h2_worker *ap__b = (e);                                 \
+       APR_RING_INSERT_TAIL((b), ap__b, h2_worker, link);      \
+    } while (0)
+
+/**
+ * Get the next worker in the list
+ * @param e The current worker
+ * @return The next worker
+ */
+#define H2_WORKER_NEXT(e)      APR_RING_NEXT((e), link)
+/**
+ * Get the previous worker in the list
+ * @param e The current worker
+ * @return The previous worker
+ */
+#define H2_WORKER_PREV(e)      APR_RING_PREV((e), link)
+
+/**
+ * Remove a worker from its list
+ * @param e The worker to remove
+ */
+#define H2_WORKER_REMOVE(e)    APR_RING_REMOVE((e), link)
+
+
+/* Create a new worker with given id, pool and attributes, callbacks
+ * callback parameter.
+ */
+h2_worker *h2_worker_create(int id,
+                            apr_pool_t *pool,
+                            apr_threadattr_t *attr,
+                            h2_worker_mplx_next_fn *get_next,
+                            h2_worker_done_fn *worker_done,
+                            void *ctx);
+
+apr_status_t h2_worker_destroy(h2_worker *worker);
+
+void h2_worker_abort(h2_worker *worker);
+
+int h2_worker_get_id(h2_worker *worker);
+
+int h2_worker_is_aborted(h2_worker *worker);
+
+apr_pool_t *h2_worker_get_pool(h2_worker *worker);
+
+apr_bucket_alloc_t *h2_worker_get_bucket_alloc(h2_worker *worker);
+
+apr_socket_t *h2_worker_get_socket(h2_worker *worker);
+
+apr_thread_t *h2_worker_get_thread(h2_worker *worker);
+
+struct apr_thread_cond_t *h2_worker_get_cond(h2_worker *worker);
+
+#endif /* defined(__mod_h2__h2_worker__) */
diff --git a/modules/http2/mod_h2/h2_workers.c b/modules/http2/mod_h2/h2_workers.c
new file mode 100644 (file)
index 0000000..94c3ccd
--- /dev/null
@@ -0,0 +1,344 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <apr_atomic.h>
+#include <apr_thread_mutex.h>
+#include <apr_thread_cond.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_mplx.h"
+#include "h2_task.h"
+#include "h2_task_queue.h"
+#include "h2_worker.h"
+#include "h2_workers.h"
+
+static int in_list(h2_workers *workers, h2_mplx *m)
+{
+    h2_mplx *e;
+    for (e = H2_MPLX_LIST_FIRST(&workers->mplxs); 
+         e != H2_MPLX_LIST_SENTINEL(&workers->mplxs);
+         e = H2_MPLX_NEXT(e)) {
+        if (e == m) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, 
+                                  h2_task **ptask, void *ctx)
+{
+    apr_status_t status;
+    h2_mplx *m = NULL;
+    h2_task *task = NULL;
+    apr_time_t max_wait, start_wait;
+    int has_more = 0;
+    h2_workers *workers = (h2_workers *)ctx;
+    
+    if (*pm && ptask != NULL) {
+        /* We have a h2_mplx instance and the worker wants the next task. 
+         * Try to get one from the given mplx. */
+        *ptask = h2_mplx_pop_task(*pm, &has_more);
+        if (*ptask) {
+            return APR_SUCCESS;
+        }
+    }
+    
+    if (*pm) {
+        /* Got a mplx handed in, but did not get or want a task from it. 
+         * Release it, as the workers reference will be wiped.
+         */
+        h2_mplx_release(*pm);
+        *pm = NULL;
+    }
+    
+    if (!ptask) {
+        /* of the worker does not want a next task, we're done.
+         */
+        return APR_SUCCESS;
+    }
+    
+    max_wait = apr_time_from_sec(apr_atomic_read32(&workers->max_idle_secs));
+    start_wait = apr_time_now();
+    
+    status = apr_thread_mutex_lock(workers->lock);
+    if (status == APR_SUCCESS) {
+        ++workers->idle_worker_count;
+        
+        ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+                     "h2_worker(%d): looking for work", h2_worker_get_id(worker));
+        
+        while (!task && !h2_worker_is_aborted(worker) && !workers->aborted) {
+            
+            /* Get the next h2_mplx to process that has a task to hand out.
+             * If it does, place it at the end of the queu and return the
+             * task to the worker.
+             * If it (currently) has no tasks, remove it so that it needs
+             * to register again for scheduling.
+             * If we run out of h2_mplx in the queue, we need to wait for
+             * new mplx to arrive. Depending on how many workers do exist,
+             * we do a timed wait or block indefinitely.
+             */
+            m = NULL;
+            while (!task && !H2_MPLX_LIST_EMPTY(&workers->mplxs)) {
+                m = H2_MPLX_LIST_FIRST(&workers->mplxs);
+                H2_MPLX_REMOVE(m);
+                
+                task = h2_mplx_pop_task(m, &has_more);
+                if (task) {
+                    if (has_more) {
+                        H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
+                    }
+                    else {
+                        has_more = !H2_MPLX_LIST_EMPTY(&workers->mplxs);
+                    }
+                    break;
+                }
+            }
+            
+            if (!task) {
+                /* Need to wait for either a new mplx to arrive.
+                 */
+                if (workers->worker_count > workers->min_size) {
+                    apr_time_t now = apr_time_now();
+                    if (now >= (start_wait + max_wait)) {
+                        /* waited long enough without getting a task. */
+                        status = APR_TIMEUP;
+                    }
+                    else {
+                        ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+                                     "h2_worker(%d): waiting signal, "
+                                     "worker_count=%d", worker->id, 
+                                     (int)workers->worker_count);
+                        status = apr_thread_cond_timedwait(workers->mplx_added,
+                                                           workers->lock, max_wait);
+                    }
+                    
+                    if (status == APR_TIMEUP) {
+                        /* waited long enough */
+                        if (workers->worker_count > workers->min_size) {
+                            ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, 
+                                         workers->s,
+                                         "h2_workers: aborting idle worker");
+                            h2_worker_abort(worker);
+                            break;
+                        }
+                    }
+                }
+                else {
+                    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+                                 "h2_worker(%d): waiting signal (eternal), "
+                                 "worker_count=%d", worker->id, 
+                                 (int)workers->worker_count);
+                    apr_thread_cond_wait(workers->mplx_added, workers->lock);
+                }
+            }
+        }
+        
+        /* Here, we either have gotten task and mplx for the worker or
+         * needed to give up with more than enough workers.
+         */
+        if (task) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                         "h2_worker(%d): start task(%s)",
+                         h2_worker_get_id(worker), task->id);
+            /* Since we hand out a reference to the worker, we increase
+             * its ref count.
+             */
+            h2_mplx_reference(m);
+            *pm = m;
+            *ptask = task;
+            
+            if (has_more && workers->idle_worker_count > 1) {
+                apr_thread_cond_signal(workers->mplx_added);
+            }
+            status = APR_SUCCESS;
+        }
+        else {
+            status = APR_EOF;
+        }
+        
+        --workers->idle_worker_count;
+        apr_thread_mutex_unlock(workers->lock);
+    }
+    
+    return status;
+}
+
+static void worker_done(h2_worker *worker, void *ctx)
+{
+    h2_workers *workers = (h2_workers *)ctx;
+    apr_status_t status = apr_thread_mutex_lock(workers->lock);
+    if (status == APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                     "h2_worker(%d): done", h2_worker_get_id(worker));
+        H2_WORKER_REMOVE(worker);
+        --workers->worker_count;
+        h2_worker_destroy(worker);
+        
+        apr_thread_mutex_unlock(workers->lock);
+    }
+}
+
+
+static apr_status_t add_worker(h2_workers *workers)
+{
+    h2_worker *w = h2_worker_create(workers->next_worker_id++,
+                                    workers->pool, workers->thread_attr,
+                                    get_mplx_next, worker_done, workers);
+    if (!w) {
+        return APR_ENOMEM;
+    }
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, workers->s,
+                 "h2_workers: adding worker(%d)", h2_worker_get_id(w));
+    ++workers->worker_count;
+    H2_WORKER_LIST_INSERT_TAIL(&workers->workers, w);
+    return APR_SUCCESS;
+}
+
+static apr_status_t h2_workers_start(h2_workers *workers) {
+    apr_status_t status = apr_thread_mutex_lock(workers->lock);
+    if (status == APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
+                      "h2_workers: starting");
+
+        while (workers->worker_count < workers->min_size
+               && status == APR_SUCCESS) {
+            status = add_worker(workers);
+        }
+        apr_thread_mutex_unlock(workers->lock);
+    }
+    return status;
+}
+
+h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool,
+                              int min_size, int max_size)
+{
+    AP_DEBUG_ASSERT(s);
+    AP_DEBUG_ASSERT(pool);
+    apr_status_t status = APR_SUCCESS;
+
+    h2_workers *workers = apr_pcalloc(pool, sizeof(h2_workers));
+    if (workers) {
+        workers->s = s;
+        workers->pool = pool;
+        workers->min_size = min_size;
+        workers->max_size = max_size;
+        apr_atomic_set32(&workers->max_idle_secs, 10);
+        
+        apr_threadattr_create(&workers->thread_attr, workers->pool);
+        
+        APR_RING_INIT(&workers->workers, h2_worker, link);
+        APR_RING_INIT(&workers->mplxs, h2_mplx, link);
+        
+        status = apr_thread_mutex_create(&workers->lock,
+                                         APR_THREAD_MUTEX_DEFAULT,
+                                         workers->pool);
+        if (status == APR_SUCCESS) {
+            status = apr_thread_cond_create(&workers->mplx_added, workers->pool);
+        }
+        
+        if (status == APR_SUCCESS) {
+            status = h2_workers_start(workers);
+        }
+        
+        if (status != APR_SUCCESS) {
+            h2_workers_destroy(workers);
+            workers = NULL;
+        }
+    }
+    return workers;
+}
+
+void h2_workers_destroy(h2_workers *workers)
+{
+    if (workers->mplx_added) {
+        apr_thread_cond_destroy(workers->mplx_added);
+        workers->mplx_added = NULL;
+    }
+    if (workers->lock) {
+        apr_thread_mutex_destroy(workers->lock);
+        workers->lock = NULL;
+    }
+    while (!H2_MPLX_LIST_EMPTY(&workers->mplxs)) {
+        h2_mplx *m = H2_MPLX_LIST_FIRST(&workers->mplxs);
+        H2_MPLX_REMOVE(m);
+    }
+    while (!H2_WORKER_LIST_EMPTY(&workers->workers)) {
+        h2_worker *w = H2_WORKER_LIST_FIRST(&workers->workers);
+        H2_WORKER_REMOVE(w);
+    }
+}
+
+apr_status_t h2_workers_register(h2_workers *workers, struct h2_mplx *m)
+{
+    apr_status_t status = apr_thread_mutex_lock(workers->lock);
+    if (status == APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, status, workers->s,
+                     "h2_workers: register mplx(%ld)", m->id);
+        if (in_list(workers, m)) {
+            status = APR_EAGAIN;
+        }
+        else {
+            H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
+            status = APR_SUCCESS;
+        }
+        
+        if (workers->idle_worker_count > 0) { 
+            apr_thread_cond_signal(workers->mplx_added);
+        }
+        else if (workers->worker_count < workers->max_size) {
+            ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
+                         "h2_workers: got %d worker, adding 1", 
+                         workers->worker_count);
+            add_worker(workers);
+        }
+        
+        apr_thread_mutex_unlock(workers->lock);
+    }
+    return status;
+}
+
+apr_status_t h2_workers_unregister(h2_workers *workers, struct h2_mplx *m)
+{
+    apr_status_t status = apr_thread_mutex_lock(workers->lock);
+    if (status == APR_SUCCESS) {
+        status = APR_EAGAIN;
+        if (in_list(workers, m)) {
+            H2_MPLX_REMOVE(m);
+            status = APR_SUCCESS;
+        }
+        apr_thread_mutex_unlock(workers->lock);
+    }
+    return status;
+}
+
+void h2_workers_set_max_idle_secs(h2_workers *workers, int idle_secs)
+{
+    if (idle_secs <= 0) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, workers->s,
+                     "h2_workers: max_worker_idle_sec value of %d"
+                     " is not valid, ignored.", idle_secs);
+        return;
+    }
+    apr_atomic_set32(&workers->max_idle_secs, idle_secs);
+}
+
diff --git a/modules/http2/mod_h2/h2_workers.h b/modules/http2/mod_h2/h2_workers.h
new file mode 100644 (file)
index 0000000..50fd6b8
--- /dev/null
@@ -0,0 +1,87 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_workers__
+#define __mod_h2__h2_workers__
+
+/* Thread pool specific to executing h2_tasks. Has a minimum and maximum 
+ * number of workers it creates. Starts with minimum workers and adds
+ * some on load, reduces the number again when idle.
+ *
+ */
+struct apr_thread_mutex_t;
+struct apr_thread_cond_t;
+struct h2_mplx;
+struct h2_task;
+struct h2_task_queue;
+
+typedef struct h2_workers h2_workers;
+
+struct h2_workers {
+    server_rec *s;
+    apr_pool_t *pool;
+    int aborted;
+    
+    int next_worker_id;
+    int min_size;
+    int max_size;
+    
+    apr_threadattr_t *thread_attr;
+    
+    APR_RING_HEAD(h2_worker_list, h2_worker) workers;
+    APR_RING_HEAD(h2_mplx_list, h2_mplx) mplxs;
+    
+    int worker_count;
+    volatile apr_uint32_t max_idle_secs;
+    volatile apr_uint32_t idle_worker_count;
+    
+    struct apr_thread_mutex_t *lock;
+    struct apr_thread_cond_t *mplx_added;
+};
+
+
+/* Create a worker pool with the given minimum and maximum number of
+ * threads.
+ */
+h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool,
+                              int min_size, int max_size);
+
+/* Destroy the worker pool and all its threads. 
+ */
+void h2_workers_destroy(h2_workers *workers);
+
+/**
+ * Registers a h2_mplx for task scheduling. If this h2_mplx runs
+ * out of tasks, it will be automatically be unregistered. Should
+ * new tasks arrive, it needs to be registered again.
+ */
+apr_status_t h2_workers_register(h2_workers *workers, 
+                                 struct h2_mplx *m);
+
+/**
+ * Remove a h2_mplx from the worker registry.
+ */
+apr_status_t h2_workers_unregister(h2_workers *workers, 
+                                   struct h2_mplx *m);
+
+/**
+ * Set the amount of seconds a h2_worker should wait for new tasks
+ * before shutting down (if there are more than the minimum number of
+ * workers).
+ */
+void h2_workers_set_max_idle_secs(h2_workers *workers, int idle_secs);
+
+#endif /* defined(__mod_h2__h2_workers__) */
diff --git a/modules/http2/mod_h2/m4/h2.m4 b/modules/http2/mod_h2/m4/h2.m4
new file mode 100644 (file)
index 0000000..8ba0b2b
--- /dev/null
@@ -0,0 +1 @@
+# just so it is not empty
diff --git a/modules/http2/mod_h2/mod_h2.c b/modules/http2/mod_h2/mod_h2.c
new file mode 100644 (file)
index 0000000..2e5f922
--- /dev/null
@@ -0,0 +1,162 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <apr_optional.h>
+#include <apr_optional_hooks.h>
+#include <apr_want.h>
+
+#include <httpd.h>
+#include <http_log.h>
+
+#include "mod_h2.h"
+
+#include <nghttp2/nghttp2.h>
+#include "h2_stream.h"
+#include "h2_alt_svc.h"
+#include "h2_conn.h"
+#include "h2_task.h"
+#include "h2_session.h"
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_h2.h"
+#include "h2_alpn.h"
+#include "h2_upgrade.h"
+#include "h2_version.h"
+
+
+static void h2_hooks(apr_pool_t *pool);
+
+AP_DECLARE_MODULE(h2) = {
+    STANDARD20_MODULE_STUFF,
+    NULL,
+    NULL,
+    h2_config_create_svr, /* func to create per server config */
+    h2_config_merge,      /* func to merge per server config */
+    h2_cmds,              /* command handlers */
+    h2_hooks
+};
+
+/* The module initialization. Called once as apache hook, before any multi
+ * processing (threaded or not) happens. It is typically at least called twice, 
+ * see
+ * http://wiki.apache.org/httpd/ModuleLife
+ * Since the first run is just a "practise" run, we want to initialize for real
+ * only on the second try. This defeats the purpose of the first dry run a bit, 
+ * since apache wants to verify that a new configuration actually will work. 
+ * So if we have trouble with the configuration, this will only be detected 
+ * when the server has already switched.
+ * On the other hand, when we initialize lib nghttp2, all possible crazy things 
+ * might happen and this might even eat threads. So, better init on the real 
+ * invocation, for now at least.
+ */
+static int h2_post_config(apr_pool_t *p, apr_pool_t *plog,
+                          apr_pool_t *ptemp, server_rec *s)
+{
+    void *data = NULL;
+    const char *mod_h2_init_key = "mod_h2_init_counter";
+    nghttp2_info *ngh2;
+    apr_status_t status;
+    (void)plog;(void)ptemp;
+    
+    apr_pool_userdata_get(&data, mod_h2_init_key, s->process->pool);
+    if ( data == NULL ) {
+        ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "initializing post config dry run");
+        apr_pool_userdata_set((const void *)1, mod_h2_init_key,
+                              apr_pool_cleanup_null, s->process->pool);
+        return APR_SUCCESS;
+    }
+    
+    ngh2 = nghttp2_version(0);
+    ap_log_error( APLOG_MARK, APLOG_INFO, 0, s,
+                 "mod_h2 (v%s, nghttp2 %s), initializing...",
+                 MOD_H2_VERSION, ngh2? ngh2->version_str : "unknown");
+    
+    switch (h2_conn_mpm_type()) {
+        case H2_MPM_EVENT:
+        case H2_MPM_WORKER:
+            /* all fine, we know these ones */
+            break;
+        case H2_MPM_PREFORK:
+            ap_log_error( APLOG_MARK, APLOG_WARNING, 0, s,
+                         "This httpd uses mpm_prefork for multiprocessing. "
+                         "Please take notice that mod_h2 always with run "
+                         "requests in a multi-threaded environment. If you "
+                         "use prefork for single-thread connection handling, "
+                         " mod_h2 might pose problems.");
+            break;
+        case H2_MPM_UNKNOWN:
+            /* ??? */
+            ap_log_error( APLOG_MARK, APLOG_ERR, 0, s,
+                         "post_config: mpm type unknown");
+            break;
+    }
+    
+    status = h2_h2_init(p, s);
+    if (status == APR_SUCCESS) {
+        status = h2_alpn_init(p, s);
+    }
+    
+    return status;
+}
+
+/* Runs once per created child process. Perform any process 
+ * related initionalization here.
+ */
+static void h2_child_init(apr_pool_t *pool, server_rec *s)
+{
+    /* Set up our connection processing */
+    apr_status_t status = h2_conn_child_init(pool, s);
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
+                      "initializing connection handling");
+    }
+}
+
+const char *h2_get_protocol(conn_rec *c)
+{
+    return h2_ctx_pnego_get(h2_ctx_get(c));
+}
+
+/* Install this module into the apache2 infrastructure.
+ */
+static void h2_hooks(apr_pool_t *pool)
+{
+    ap_log_perror(APLOG_MARK, APLOG_INFO, 0, pool, "installing hooks");
+    
+    static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
+    
+    /* Run once after configuration is set, but before mpm children initialize.
+     */
+    ap_hook_post_config(h2_post_config, mod_ssl, NULL, APR_HOOK_MIDDLE);
+    
+    /* Run once after a child process has been created.
+     */
+    ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
+
+    h2_h2_register_hooks();
+    h2_alpn_register_hooks();
+    h2_upgrade_register_hooks();
+    h2_task_register_hooks();
+
+    h2_alt_svc_register_hooks();
+    
+    /* We offer a function to other modules that lets them retrieve
+     * the h2 protocol used on a connection (if any).
+     */
+    APR_REGISTER_OPTIONAL_FN(h2_get_protocol);
+}
+
+
diff --git a/modules/http2/mod_h2/mod_h2.h b/modules/http2/mod_h2/mod_h2.h
new file mode 100644 (file)
index 0000000..d27c8b2
--- /dev/null
@@ -0,0 +1,28 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mod_h2_mod_h2_h
+#define mod_h2_mod_h2_h
+
+const char *h2_get_protocol(conn_rec *c);
+
+
+/** 
+ * An optional function which returns the h2 protocol used on the given
+ * connection and NULL if no h2* protocol is active on it.
+ */
+APR_DECLARE_OPTIONAL_FN(const char *, h2_get_protocol, (conn_rec*));
+
+#endif
diff --git a/modules/http2/sandbox/.gitignore b/modules/http2/sandbox/.gitignore
new file mode 100644 (file)
index 0000000..1712984
--- /dev/null
@@ -0,0 +1,2 @@
+install
+gen
diff --git a/modules/http2/sandbox/Makefile.am b/modules/http2/sandbox/Makefile.am
new file mode 100644 (file)
index 0000000..698eabe
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+SUBDIRS         = httpd nghttp2 test
+DIST_SUBDIRS    = httpd nghttp2 test
+
+INSTALL_DIR     = install
+
+EXTRA_DIST = \
+    httpd/Makefile \
+    httpd/*.sh \
+    httpd/patches/* \
+    httpd/packages/* \
+    nghttp2/Makefile \
+    test/Makefile \
+    test/*.sh \
+    test/*.txt \
+    test/htdocs \
+    test/conf \
+    test/clients/Makefile
+
+
+.PHONY: test loadtest start stop restart
+
+start:
+       make -C test start
+
+restart:
+       make -C test restart
+
+stop:
+       make -C test stop
+
+test:
+       make -C test test
+
+loadtest:
+       make -C test loadtest
+
+mpm_worker:
+       make -C test mpm_worker
+
+mpm_event:
+       make -C test mpm_event
+
+mpm_prefork:
+       make -C test mpm_prefork
diff --git a/modules/http2/sandbox/httpd/.gitignore b/modules/http2/sandbox/httpd/.gitignore
new file mode 100644 (file)
index 0000000..4f62b84
--- /dev/null
@@ -0,0 +1 @@
+gen
diff --git a/modules/http2/sandbox/httpd/Makefile b/modules/http2/sandbox/httpd/Makefile
new file mode 100644 (file)
index 0000000..45cc119
--- /dev/null
@@ -0,0 +1,319 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+OS           = $(shell uname -s)
+MACHINE      = $(shell uname -m)
+
+
+APR_VERSION  = 1.5.2
+APR_DIR      = apr-$(APR_VERSION)
+APR_TAR      = $(APR_DIR).tar.gz
+APR_URL      = http://archive.apache.org/dist/apr/$(APR_TAR)
+
+APR-UTIL_VERSION = 1.5.4
+APR-UTIL_DIR = apr-util-$(APR-UTIL_VERSION)
+APR-UTIL_TAR = $(APR-UTIL_DIR).tar.gz
+APR-UTIL_URL = http://archive.apache.org/dist/apr/$(APR-UTIL_TAR)
+
+HTTPD_VERSION = 2.4.12
+HTTPD_DIR    = httpd-$(HTTPD_VERSION)
+HTTPD_TAR    = httpd-$(HTTPD_VERSION).tar.gz
+HTTPD_URL    = https://archive.apache.org/dist/httpd/$(HTTPD_TAR)
+
+HTTPD_CONF   = --enable-mpms-shared=all --with-included-apr
+HTTPD_DEPS   = $(GEN)/$(HTTPD_DIR)/.apr-extracted
+
+PCRE_VERSION = 8.36
+PCRE_DIR     = pcre-$(PCRE_VERSION)
+PCRE_TAR     = $(PCRE_DIR).tar.gz
+PCRE_URL     = http://sourceforge.net/projects/pcre/files/pcre/$(PCRE_VERSION)/$(PCRE_TAR)
+
+OPENSSL_BASE = http://www.openssl.org
+OPENSSL_DIR = openssl-latest
+OPENSSL_TAR = $(OPENSSL_DIR).tar.gz
+OPENSSL_URL = $(OPENSSL_BASE)/source/latest.tar.gz
+OPENSSL_CONF_CMD = ./config
+OPENSSL_CONF =  shared -fPIC
+
+FCGI_VERSION = 2.3.9
+FCGI_DIR     = mod_fcgid-$(FCGI_VERSION)
+FCGI_TAR     = $(FCGI_DIR).tar.gz
+FCGI_URL     = https://archive.apache.org/dist/httpd/mod_fcgid/$(FCGI_TAR)
+
+GEN          = gen
+INST_DIR     = ../install
+BLD_PREFIX   = $(shell dirname $$PWD)/install
+
+CURL_OPTS    = --progress-bar
+
+# For OS X, we drag our own PCRE lib in as the one installed is not
+# recognized by the httpd configure (and pbly rightly so).
+# On other OS, we expect a proper pcre installation
+#
+ifeq ($(OS),Darwin)
+       # we need our own apr + apr-util
+       HTTPD_CONF  += --with-crypto
+       # we need our own pcre
+       HTTPD_DEPS  += $(INST_DIR)/.pcre-installed
+       HTTPD_CONF  += --with-pcre=$(BLD_PREFIX)
+endif
+
+OPENSSL_VERSION = $(shell openssl version -v | sed -e 's/OpenSSL *//g' -e 's/[a-z]* .*//g')
+
+ifeq ($(OPENSSL_VERSION), $(filter $(OPENSSL_VERSION),0.9.7 0.9.8 1.0.0 1.0.1))
+       # Very old openssl without alpn support installed, need a newer one
+       HTTPD_DEPS  += $(INST_DIR)/.openssl-installed
+    HTTPD_CONF  += --enable-ssl --with-ssl=$(BLD_PREFIX)
+       ifeq ($(OS),Darwin)
+        OPENSSL_CONF_CMD = ./Configure
+        OPENSSL_CONF = darwin64-x86_64-cc
+        HTTPD_CONF  += --enable-ssl-staticlib-deps
+       endif
+endif
+
+HTTP_PORT = 12345
+HTTPS_PORT = 12346
+
+all: install
+
+distdir:
+
+dirs:
+       @mkdir -p $(GEN)/build
+
+clean:
+       @rm -rf $(GEN)/$(PCRE_DIR) $(GEN)/$(OPENSSL_DIR) $(GEN)/$(HTTPD_DIR) \
+        $(GEN)/build $(INST_DIR)/.httpd-installed
+
+distclean:
+       @rm -rf $(GEN)
+
+install: \
+    $(INST_DIR)/.httpd-installed \
+    $(INST_DIR)/.modules-installed
+
+setup: $(INST_DIR)/.httpd-setup
+
+copy-mod_ssl:
+       @cp mod_ssl-alpn/*.[ch] $(GEN)/$(HTTPD_DIR)/modules/ssl
+       @rm -f $(GEN)/$(HTTPD_DIR)/.httpd-built
+
+################################################################################
+# Install the local httpd
+#
+$(INST_DIR)/.httpd-installed:  $(GEN)/$(HTTPD_DIR)/.httpd-built
+       @mkdir -p $(INST_DIR)
+       @echo -n installing httpd locally...
+       @cd $(GEN)/$(HTTPD_DIR)/ && make install
+       @echo done.
+       @touch $(INST_DIR)/.httpd-installed
+
+
+################################################################################
+# Build the local, patched httpd
+#
+$(GEN)/$(HTTPD_DIR)/.httpd-built: \
+               $(GEN)/$(HTTPD_DIR)/.httpd-configured
+       @echo -n building httpd...
+       @cd $(GEN)/$(HTTPD_DIR)/ && make
+       @echo done.
+       @touch $(GEN)/$(HTTPD_DIR)/.httpd-built
+
+################################################################################
+# Configure the local httpd sources
+#
+$(GEN)/$(HTTPD_DIR)/configure: 
+       cd $(GEN)/$(HTTPD_DIR)/ && ./buildconf
+
+$(GEN)/$(HTTPD_DIR)/.httpd-configured: \
+               $(HTTPD_DEPS) \
+               $(GEN)/$(HTTPD_DIR)/configure \
+               $(GEN)/$(HTTPD_DIR)/.httpd-patched
+       @echo -n configuring httpd...
+       cd $(GEN)/$(HTTPD_DIR)/ && \
+       ./configure --prefix=$(BLD_PREFIX) $(HTTPD_CONF)
+       @echo done.
+       @touch $(GEN)/$(HTTPD_DIR)/.httpd-configured
+
+################################################################################
+# Patch the local httpd sources
+#
+$(GEN)/$(HTTPD_DIR)/.httpd-patched: \
+               $(GEN)/$(HTTPD_DIR)/.httpd-extracted
+       @echo applying patches...
+       @cd gen/$(HTTPD_DIR) && patch -p0 < ../../patches/httpd-2.4.12-alpn-v5.patch
+       @cd gen/$(HTTPD_DIR) && patch -p0 < ../../patches/sni_misdirect.patch
+       @echo httpd patched.
+       @touch $(GEN)/$(HTTPD_DIR)/.httpd-patched
+#@cd gen/$(HTTPD_DIR) && patch -p0 < ../../patches/alpn-2.4.x.unified.diff.patch
+
+################################################################################
+# Extract apache source tree
+#
+$(GEN)/$(HTTPD_DIR)/.httpd-extracted: \
+               $(GEN)/$(HTTPD_TAR)
+       @rm -rf $(GEN)/$(HTTPD_DIR)
+       @echo -n extracting httpd packages...
+       @cd $(GEN) && tar xfz $(HTTPD_TAR)
+       @echo done.
+       @touch $(GEN)/$(HTTPD_DIR)/.httpd-extracted
+
+################################################################################
+# Extract apr + apr-util into apache source tree (if needed)
+#
+$(GEN)/$(HTTPD_DIR)/.apr-extracted: \
+               $(GEN)/$(HTTPD_DIR)/.httpd-extracted \
+               $(GEN)/$(APR_TAR) $(GEN)/$(APR-UTIL_TAR)
+       @rm -rf $(GEN)/$(HTTPD_DIR)/srclib/apr $(GEN)/$(HTTPD_DIR)/srclib/apr-util
+       @echo -n extracting apr packages...
+       @cd gen/$(HTTPD_DIR)/srclib; tar xfz ../../$(APR_TAR) && mv $(APR_DIR) apr
+       @cd gen/$(HTTPD_DIR)/srclib; tar xfz ../../$(APR-UTIL_TAR) && mv $(APR-UTIL_DIR) apr-util
+       @echo done.
+       @touch $(GEN)/$(HTTPD_DIR)/.apr-extracted
+
+################################################################################
+# Retrieve apache sources (and apr + apr-util if needed)
+#
+$(GEN)/$(HTTPD_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) $(HTTPD_URL) > $(GEN)/$(HTTPD_TAR)
+
+$(GEN)/$(APR_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) $(APR_URL) > $(GEN)/$(APR_TAR)
+
+$(GEN)/$(APR-UTIL_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) $(APR-UTIL_URL) > $(GEN)/$(APR-UTIL_TAR)
+
+################################################################################
+# Build + install additional modules
+#
+$(INST_DIR)/.modules-installed: \
+        $(INST_DIR)/.httpd-installed \
+        $(INST_DIR)/.mod_fcgid-installed
+       @touch $(INST_DIR)/.modules-installed
+
+################################################################################
+# Build + install mod_fcgid
+#
+$(INST_DIR)/.mod_fcgid-installed: \
+        $(INST_DIR)/.httpd-installed \
+        $(GEN)/$(FCGI_DIR)/.mod_fcgid-built
+       @echo -n installing mod_fcgid locally...
+       @cd $(GEN)/$(FCGI_DIR) && make install
+       @echo done.
+       @touch $(INST_DIR)/.mod_fcgid-installed
+
+$(GEN)/$(FCGI_DIR)/.mod_fcgid-built: \
+        $(GEN)/$(FCGI_DIR)/.mod_fcgid-configured
+       @echo -n building mod_fcgid...
+       @cd $(GEN)/$(FCGI_DIR) && make
+       @echo done.
+       @touch $(GEN)/$(FCGI_DIR)/.mod_fcgid-built
+
+
+$(GEN)/$(FCGI_DIR)/.mod_fcgid-configured: \
+        $(GEN)/$(FCGI_DIR)/.mod_fcgid-extracted
+       @echo -n configuring mod_fcgid...
+       @cd $(GEN)/$(FCGI_DIR) &&  APXS=../../$(INST_DIR)/bin/apxs ./configure.apxs
+       @echo done.
+       @touch $(GEN)/$(FCGI_DIR)/.mod_fcgid-configured
+
+$(GEN)/$(FCGI_DIR)/.mod_fcgid-extracted: \
+        $(GEN)/$(FCGI_TAR)
+       @rm -rf $(GEN)/$(FCGI_DIR)
+       @echo -n downloading and extracting mod_fcgid...
+       @cd gen; tar xfz $(FCGI_TAR)
+       @echo done.
+       @touch $(GEN)/$(FCGI_DIR)/.mod_fcgid-extracted
+
+$(GEN)/$(FCGI_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) $(FCGI_URL) > $(GEN)/$(FCGI_TAR)
+
+
+################################################################################
+# Build + install a local opensll library (if needed)
+#
+$(INST_DIR)/.openssl-installed: \
+               $(GEN)/$(OPENSSL_DIR)/.built
+       @mkdir -p $(INST_DIR)
+       @echo -n installing openssl locally, may take some time...
+       @cd $(GEN)/$(OPENSSL_DIR) && make install_sw >> ../build.log
+       @echo done.
+       @touch $(INST_DIR)/.openssl-installed
+
+$(GEN)/$(OPENSSL_DIR)/.built: \
+               $(GEN)/$(OPENSSL_DIR)/.configured
+       @echo -n building openssl locally...
+       @cd $(GEN)/$(OPENSSL_DIR) && make
+       @echo done.
+       @touch $(GEN)/$(OPENSSL_DIR)/.built
+
+$(GEN)/$(OPENSSL_DIR)/.configured: \
+        $(GEN)/$(OPENSSL_DIR)/.patched
+       @echo -n configuring openssl...
+       cd $(GEN)/$(OPENSSL_DIR) && $(OPENSSL_CONF_CMD) --openssldir=$(BLD_PREFIX) $(OPENSSL_CONF)
+       @echo done.
+       @touch $(GEN)/$(OPENSSL_DIR)/.configured
+
+$(GEN)/$(OPENSSL_DIR)/.patched: \
+               $(GEN)/$(OPENSSL_DIR)/.extracted
+       @echo applying patches...
+       # experimental patch to solve SNI+ALPN callback ordering in openssl
+       # not vital with latest httpd-alpn patch. We leave this out of our sandbox
+       # to have it running against an unpatched ssl for better test coverage.
+       #@cd gen/$(OPENSSL_DIR) && patch -p1 < ../../patches/openssl-1.0.2-alpn.patch
+       @echo openssl patched.
+       @touch $(GEN)/$(OPENSSL_DIR)/.patched
+
+$(GEN)/$(OPENSSL_DIR)/.extracted:
+       @echo -n downloading and extracting openssl...
+       @mkdir -p $(GEN)
+       @bash get-openssl-latest.sh $(OPENSSL_URL) $(GEN)/$(OPENSSL_DIR)
+       @echo done.
+       @touch $(GEN)/$(OPENSSL_DIR)/.extracted
+
+################################################################################
+# Build + install a local pcre library (if needed)
+#
+$(INST_DIR)/.pcre-installed: \
+               $(GEN)/$(PCRE_DIR)/.pcre-built
+       @mkdir -p $(INST_DIR)
+       @echo -n installing pcre locally...
+       @cd $(GEN)/$(PCRE_DIR) && make install >> ../build.log
+       @echo done.
+       @touch $(INST_DIR)/.pcre-installed
+
+$(GEN)/$(PCRE_DIR)/.pcre-built: \
+               $(GEN)/$(PCRE_DIR)/.pcre-configured
+       @echo -n building pcre locally...
+       @cd $(GEN)/$(PCRE_DIR) && make >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(PCRE_DIR)/.pcre-built
+
+$(GEN)/$(PCRE_DIR)/.pcre-configured: $(GEN)/$(PCRE_TAR)
+       @rm -rf $(GEN)/$(PCRE_DIR)
+       @cd $(GEN) && tar xfz $(PCRE_TAR)
+       @echo -n configuring pcre...
+       @cd $(GEN)/$(PCRE_DIR) && ./configure --prefix=$(BLD_PREFIX) >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(PCRE_DIR)/.pcre-configured
+
+$(GEN)/$(PCRE_TAR): packages/$(PCRE_TAR)
+       @mkdir -p $(GEN)
+       @cp packages/$(PCRE_TAR) $(GEN)
diff --git a/modules/http2/sandbox/httpd/get-openssl-latest.sh b/modules/http2/sandbox/httpd/get-openssl-latest.sh
new file mode 100755 (executable)
index 0000000..558575f
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+#  get-openssl-latest.sh
+#  mod-h2
+#
+#  Created by Stefan Eissing on 24.03.15.
+#  Inspects the given URL for redirects and downloads tar file
+#  to real name. Symlinks given tar name to real name
+
+URL=$1
+DESTDIR=$2
+
+usage() {
+    echo "$@"
+    echo "usage: $(basename $0) url dir"
+    echo "  download and extract latest openssl url to given directory,"
+    echo "  using real version name and symlinks"
+    exit 2
+}
+
+fail() {
+    echo "$@"
+    exit 1
+}
+
+[ -z "$URL" ] && usage "url parameter missing"
+[ -z "$DESTDIR" ] && usage "dir parameter missing"
+
+GEN=$(dirname "$DESTDIR")
+[ -d "$GEN" ] || fail "destination dir $GEN does not exist"
+
+curl -s -D "$GEN"/xxx-header $URL > "$GEN"/xxx-content || fail "error downloading $URL"
+REAL_URL=$( fgrep -i location: < "$GEN"/xxx-header | sed s',.*: ,,' | tr -d '\r\n' )
+
+case "$REAL_URL" in
+    */var/www/*)
+        # currently openssl returns the wrong path - yet the correct tar name
+        REAL_TAR=$(basename $REAL_URL)
+        REAL_URL=$(dirname $URL)/$REAL_TAR
+        ;;
+    *)
+        REAL_TAR=$(basename $REAL_URL)
+        ;;
+esac
+
+echo "downloading latest openssl from $REAL_URL"
+
+REAL_DIR=$(basename $REAL_TAR .tar.gz)
+rm -f "$GEN/$REAL_TAR" "$DESTDIR" "$GEN"/xxx-header "$GEN"/xxx-content
+
+curl -'#' "$REAL_URL" > "$GEN/$REAL_TAR" || fail "error downloading $REAL_URL"
+(cd "$GEN" && tar xfz "$REAL_TAR") || fail "error extracting $GEN/$REAL_TAR"
+[ -d "$GEN/$REAL_DIR" ] || fail "expected directory $GEN/$REAL_DIR"
+(cd $GEN && ln -s "$REAL_DIR" $(basename $DESTDIR))
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile b/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile
new file mode 100644 (file)
index 0000000..6ba1efd
--- /dev/null
@@ -0,0 +1,25 @@
+top_srcdir   = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10
+top_builddir = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10
+srcdir       = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10/modules/ssl
+builddir     = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10/modules/ssl
+VPATH        = /Users/sei/projects/mod-h2/httpd/gen/httpd-2.4.10/modules/ssl
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+#   standard stuff
+#
+
+include $(top_srcdir)/build/special.mk
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile.in b/modules/http2/sandbox/httpd/mod_ssl-alpn/Makefile.in
new file mode 100644 (file)
index 0000000..4395bc3
--- /dev/null
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+#   standard stuff
+#
+
+include $(top_srcdir)/build/special.mk
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/NWGNUmakefile b/modules/http2/sandbox/httpd/mod_ssl-alpn/NWGNUmakefile
new file mode 100644 (file)
index 0000000..933180f
--- /dev/null
@@ -0,0 +1,327 @@
+#
+# This Makefile requires the environment var OSSLSDK
+# pointing to the base directory of your OpenSSL SDK.
+# If you want to use the Novell NTLS SDK instead then
+# define NTLSSDK pointing to the base directory of the
+# SDK, and also set USE_NTLS=1
+#
+
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+       $(EOLIST)
+
+#
+# Get the 'head' of the build environment.  This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)/build/NWGNUhead.inc
+
+#
+# build this level's files
+#
+# Make sure all needed macro's are defined
+#
+
+ifeq "$(USE_NTLS)" "1"
+SSL_INC = $(NTLSSDK)/inc
+SSL_LIB = $(NTLSSDK)/imp
+SSL_BIN = $(NTLSSDK)/bin
+SSL_APP = $(NTLSSDK)/apps
+ifneq "$(wildcard $(SSL_INC)/openssl/opensslv.h)" "$(SSL_INC)/openssl/opensslv.h"
+$(error '$(NTLSSDK)' does NOT point to a valid NTLS SDK!)
+endif
+else
+SSL_INC = $(OSSLSDK)/outinc_nw_libc
+SSL_LIB = $(OSSLSDK)/out_nw_libc
+SSL_BIN = $(OSSLSDK)/out_nw_libc
+SSL_APP = $(OSSLSDK)/apps
+ifneq "$(wildcard $(SSL_INC)/openssl/opensslv.h)" "$(SSL_INC)/openssl/opensslv.h"
+$(error '$(OSSLSDK)' does NOT point to a valid OpenSSL SDK!)
+endif
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS       += \
+                       $(SSL_INC) \
+                       $(APR)/include \
+                       $(APRUTIL)/include \
+                       $(AP_WORK)/include \
+                       $(AP_WORK)/modules/cache \
+                       $(AP_WORK)/modules/generators \
+                       $(AP_WORK)/server/mpm/NetWare \
+                       $(NWOS) \
+                       $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS                += \
+                       $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES       += \
+                       -DHAVE_OPENSSL \
+                       $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS                += \
+                       -l $(SSL_LIB) \
+                       $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS       += \
+                       $(EOLIST)
+
+XCFLAGS                += \
+                       $(EOLIST)
+
+XDEFINES       += \
+                       $(EOLIST)
+
+XLFLAGS                += \
+                       $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS       += \
+                       $(EOLIST)
+
+XCFLAGS                += \
+                       $(EOLIST)
+
+XDEFINES       += \
+                       $(EOLIST)
+
+XLFLAGS                += \
+                       $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS       += \
+                       $(EOLIST)
+
+XCFLAGS                += \
+                       $(EOLIST)
+
+XDEFINES       += \
+                       $(EOLIST)
+
+XLFLAGS                += \
+                       $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME       = mod_ssl
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+ifeq "$(USE_NTLS)" "1"
+NLM_DESCRIPTION        = Apache $(VERSION_STR) SSL module (NTLS)
+else
+NLM_DESCRIPTION        = Apache $(VERSION_STR) SSL module (OpenSSL)
+endif
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME        = $(NLM_NAME)
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION    =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM  =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM   =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM  =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS      =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA                =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+       $(OBJDIR)/$(NLM_NAME).nlm \
+       $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+       $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs := $(patsubst %.c,$(OBJDIR)/%.o,$(wildcard *.c))
+
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+       $(PRELUDE) \
+       $(EOLIST)
+
+ifneq "$(USE_NTLS)" "1"
+FILES_nlm_libs += \
+       $(SSL_LIB)/crypto.lib \
+       $(SSL_LIB)/ssl.lib \
+       $(EOLIST)
+endif
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+       Apache2 \
+       Libc \
+       $(EOLIST)
+
+ifeq "$(USE_NTLS)" "1"
+FILES_nlm_modules += ntls \
+       $(EOLIST)
+endif
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+       @libc.imp \
+       @aprlib.imp \
+       @httpd.imp \
+       $(EOLIST)
+
+# Don't link with Winsock if standard sockets are being used
+ifneq "$(USE_STDSOCKETS)" "1"
+FILES_nlm_Ximports += @ws2nlm.imp \
+       $(EOLIST)
+endif
+
+ifeq "$(USE_NTLS)" "1"
+FILES_nlm_Ximports += @ntls.imp \
+       $(EOLIST)
+else
+FILES_nlm_Ximports += \
+       GetProcessSwitchCount \
+       RunningProcess \
+       GetSuperHighResolutionTimer \
+       $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+       ssl_module \
+       $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+       $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place.  (See $(AP_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+       $(call COPY,$(OBJDIR)/*.nlm,        $(INSTALLBASE)/modules/)
+       $(call COPY,$(SSL_BIN)/openssl.nlm, $(INSTALLBASE)/bin/)
+       $(call COPY,$(SSL_APP)/openssl.cnf, $(INSTALLBASE)/bin/)
+
+#
+# Any specialized rules here
+#
+vpath %.c $(AP_WORK)/modules/arch/netware
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APBUILD)/NWGNUtail.inc
+
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/README b/modules/http2/sandbox/httpd/mod_ssl-alpn/README
new file mode 100644 (file)
index 0000000..c46377f
--- /dev/null
@@ -0,0 +1,106 @@
+SYNOPSIS
+
+ This Apache module provides strong cryptography for the Apache 2 webserver
+ via the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS
+ v1) protocols by the help of the SSL/TLS implementation library OpenSSL which
+ is based on SSLeay from Eric A. Young and Tim J. Hudson. 
+
+ The mod_ssl package was created in April 1998 by Ralf S. Engelschall 
+ and was originally derived from software developed by Ben Laurie for 
+ use in the Apache-SSL HTTP server project.  The mod_ssl implementation 
+ for Apache 1.3 continues to be supported by the modssl project 
+ <http://www.modssl.org/>.
+
+SOURCES
+
+ See the top-level LAYOUT file for file descriptions.
+
+ The source files are written in clean ANSI C and pass the ``gcc -O -g
+ -ggdb3 -Wall -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes
+ -Wmissing-declarations -Wnested-externs -Winline'' compiler test
+ (assuming `gcc' is GCC 2.95.2 or newer) without any complains. When
+ you make changes or additions make sure the source still passes this
+ compiler test.
+
+FUNCTIONS
+  
+ Inside the source code you will be confronted with the following types of
+ functions which can be identified by their prefixes:
+
+   ap_xxxx() ............... Apache API function
+   ssl_xxxx() .............. mod_ssl function
+   SSL_xxxx() .............. OpenSSL function (SSL library)
+   OpenSSL_xxxx() .......... OpenSSL function (SSL library)
+   X509_xxxx() ............. OpenSSL function (Crypto library)
+   PEM_xxxx() .............. OpenSSL function (Crypto library)
+   EVP_xxxx() .............. OpenSSL function (Crypto library)
+   RSA_xxxx() .............. OpenSSL function (Crypto library)
+
+DATA STRUCTURES
+
+ Inside the source code you will be confronted with the following
+ data structures:
+
+   server_rec .............. Apache (Virtual) Server
+   conn_rec ................ Apache Connection
+   request_rec ............. Apache Request
+   SSLModConfig ............ mod_ssl (Global)  Module Configuration
+   SSLSrvConfig ............ mod_ssl (Virtual) Server Configuration
+   SSLDirConfig ............ mod_ssl Directory Configuration
+   SSLConnConfig ........... mod_ssl Connection Configuration
+   SSLFilterRec ............ mod_ssl Filter Context
+   SSL_CTX ................. OpenSSL Context
+   SSL_METHOD .............. OpenSSL Protocol Method
+   SSL_CIPHER .............. OpenSSL Cipher
+   SSL_SESSION ............. OpenSSL Session
+   SSL ..................... OpenSSL Connection
+   BIO ..................... OpenSSL Connection Buffer
+
+ For an overview how these are related and chained together have a look at the
+ page in README.dsov.{fig,ps}. It contains overview diagrams for those data
+ structures. It's designed for DIN A4 paper size, but you can easily generate
+ a smaller version inside XFig by specifing a magnification on the Export
+ panel.
+
+INCOMPATIBILITIES
+
+ The following intentional incompatibilities exist between mod_ssl 2.x
+ from Apache 1.3 and this mod_ssl version for Apache 2:
+
+ o The complete EAPI-based SSL_VENDOR stuff was removed.
+ o The complete EAPI-based SSL_COMPAT stuff was removed.
+ o The <IfDefine> variable MOD_SSL is no longer provided automatically 
+
+MAJOR CHANGES 
+
+ For a complete history of changes for Apache 2 mod_ssl, see the 
+ CHANGES file in the top-level directory.  The following 
+ is a condensed summary of the major changes were made between 
+ mod_ssl 2.x from Apache 1.3 and this mod_ssl version for Apache 2:
+
+ o The DBM based session cache is now based on APR's DBM API only.
+ o The shared memory based session cache is now based on APR's APIs.
+ o SSL I/O is now implemented in terms of filters rather than BUFF
+ o Eliminated ap_global_ctx. Storing Persistant information in 
+   process_rec->pool->user_data. The ssl_pphrase_Handle_CB() and 
+   ssl_config_global_* () functions have an extra parameter now - 
+   "server_rec *" -  which is used to retrieve the SSLModConfigRec.
+ o Properly support restarts, allowing mod_ssl to be added to a server
+   that is already running and to change server certs/keys on restart
+ o Various performance enhancements
+ o proxy support is no longer an "extension", much of the mod_ssl core
+   was re-written (ssl_engine_{init,kernel,config}.c) to be generic so
+   it could be re-used in proxy mode.
+   - the optional function ssl_proxy_enable is provide for mod_proxy
+     to enable proxy support
+   - proxy support now requires 'SSLProxyEngine on' to be configured
+   - proxy now supports SSLProxyCARevocation{Path,File} in addition to
+     the original SSLProxy* directives
+ o per-directory SSLCACertificate{File,Path} is now thread-safe but
+   requires SSL_set_cert_store patch to OpenSSL
+ o the ssl_engine_{ds,ext}.c source files are obsolete and no longer
+   exist
+
+TODO
+
+ See the top-level STATUS file for current efforts and goals.
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/README.dsov.fig b/modules/http2/sandbox/httpd/mod_ssl-alpn/README.dsov.fig
new file mode 100644 (file)
index 0000000..d8d03db
--- /dev/null
@@ -0,0 +1,346 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+Letter  
+100.00
+Single
+-2
+1200 2
+0 32 #616561
+0 33 #b6b2b6
+0 34 #f7f3f7
+0 35 #cfcfcf
+0 36 #ffffff
+6 6345 2835 7155 3150
+6 6345 2970 7110 3150
+4 0 0 200 0 20 8 0.0000 4 120 585 6345 3105 "ssl_module")\001
+-6
+4 0 0 200 0 20 8 0.0000 4 120 660 6345 2970 ap_ctx_get(...,\001
+-6
+6 10800 2610 12240 3060
+4 0 0 200 0 20 8 0.0000 4 120 1170 10800 2745 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 795 10800 2880 ->per_dir_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 10800 3015 &ssl_module)\001
+-6
+6 7920 4770 9135 4995
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        9135 4995 7920 4995 7920 4770 9135 4770 9135 4995
+4 0 0 100 0 18 12 0.0000 4 180 1065 8010 4950 request_rec\001
+-6
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        6975 3330 7425 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7200 4230 9450 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7875 4905 7200 5220
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        6750 5130 6750 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        6705 5445 7155 6120
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7875 4815 7200 4590
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9585 2565 11475 4230
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        10170 5130 11835 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7920 6075 9855 5400
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9990 5445 10935 5625
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        10215 5310 10935 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11925 4590 11925 5085
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9810 5490 9810 6840
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9945 5445 10935 6030
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        8865 4725 10800 2565
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        675 6075 5850 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        675 6525 675 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5850 6075 5850 6525
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        900 5625 5625 5625
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1125 5175 5400 5175
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1350 4725 5175 4725
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1575 4275 4950 4275
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        1800 3825 4725 3825
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2025 3375 4500 3375
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2250 2925 4275 2925
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2475 2475 4050 2475
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2700 2025 3825 2025
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+        2925 1575 3600 1575
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        900 6075 900 5625
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1125 6525 1125 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1350 5175 1350 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1575 4725 1575 4275
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1800 6525 1800 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2025 3825 2025 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2250 3375 2250 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2475 2925 2475 2475
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5625 5625 5625 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5400 5175 5400 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        5175 4725 5175 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4950 4275 4950 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4725 3825 4725 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4500 3375 4500 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4275 2925 4275 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        4050 2475 4050 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2700 6525 2700 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        3825 2025 3825 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 0 1.00 60.00 120.00
+        3600 1575 3600 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2925 2025 2925 1575
+2 1 0 4 0 0 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 4.00 60.00 120.00
+        540 6525 6300 6525
+2 3 0 1 7 7 800 0 20 0.000 0 0 -1 0 0 9
+        675 6525 5850 6525 5850 6075 5625 6075 5625 5625 900 5625
+        900 6075 675 6075 675 6525
+2 3 0 1 34 34 700 0 20 0.000 0 0 -1 0 0 13
+        1125 6525 5355 6525 5400 5175 5175 5175 5175 4725 4950 4725
+        4950 4275 1575 4275 1575 4725 1350 4725 1350 5175 1125 5175
+        1125 6525
+2 3 0 1 35 35 500 0 20 0.000 0 0 -1 0 0 17
+        1800 6525 4725 6525 4725 3825 4500 3825 4500 3375 4275 3375
+        4275 2925 4050 2925 4050 2475 2475 2475 2475 2925 2250 2925
+        2250 3375 2025 3375 2025 3825 1800 3825 1800 6525
+2 3 0 1 33 33 400 0 20 0.000 0 0 -1 0 0 9
+        2700 6525 3825 6525 3825 2025 3600 2025 3600 1575 2925 1575
+        2925 2025 2700 2025 2700 6525
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        2700 6750 3825 6750
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        1125 7200 5400 7200
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        1800 6975 4725 6975
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+       2 0 1.00 60.00 120.00
+       2 0 1.00 60.00 120.00
+        675 7425 5850 7425
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        675 6570 675 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        1125 6570 1125 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        1800 6570 1800 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        2700 6570 2700 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        3825 6570 3825 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        4725 6570 4725 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        5400 6570 5400 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+        5850 6570 5850 7650
+2 4 0 2 0 7 100 0 -1 0.000 0 0 20 0 0 5
+        12600 8550 450 8550 450 225 12600 225 12600 8550
+2 4 0 1 0 34 200 0 20 0.000 0 0 20 0 0 5
+        12600 1350 450 1350 450 225 12600 225 12600 1350
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        10170 2475 8775 2475 8775 2250 10170 2250 10170 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        11925 2475 10575 2475 10575 2250 11925 2250 11925 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12375 4500 11430 4500 11430 4275 12375 4275 12375 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12375 5400 10980 5400 10980 5175 12375 5175 12375 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        10170 5400 9675 5400 9675 5175 10170 5175 10170 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7875 6300 7200 6300 7200 6075 7875 6075 7875 6300
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        8190 2475 6750 2475 6750 2250 8190 2250 8190 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7605 3600 6300 3600 6300 3375 7605 3375 7605 3600
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7335 4500 6300 4500 6300 4275 7335 4275 7335 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        7200 5400 6300 5400 6300 5175 7200 5175 7200 5400
+2 1 0 6 7 7 600 0 -1 0.000 0 0 -1 0 0 2
+        9450 4500 6075 1935
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+        9450 4500 12465 2205
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+        9450 4500 9450 7785
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9630 5310 7245 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11385 4365 7380 4365
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12240 5805 10980 5805 10980 5580 12240 5580 12240 5805
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12375 6210 10980 6210 10980 5985 12375 5985 12375 6210
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11205 6885 9900 5445
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        12285 7155 10530 7155 10530 6930 12285 6930 12285 7155
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+        10170 7155 9630 7155 9630 6930 10170 6930 10170 7155
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+        12510 6435 9450 6435
+2 1 0 1 0 34 300 0 20 0.000 0 0 7 1 0 4
+       1 1 1.00 60.00 120.00
+        12375 4455 12510 4635 12510 6210 11970 6885
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        9850 5143 9175 4918
+3 1 0 1 34 34 800 0 20 0.000 0 0 0 41
+        7380 1710 6390 2115 5535 2115 6075 3015 5670 3465 6165 3915
+        5715 4410 6030 5040 6030 5310 6480 5715 6390 6255 6975 6300
+        7065 6975 7965 6750 8100 7560 8955 7290 9360 7740 9720 7560
+        10755 8145 12060 8280 12375 7650 12420 7200 12510 7065 12330 6660
+        12510 6390 12420 5940 12375 5400 12510 5220 12510 4725 12600 4275
+        12375 3645 12105 3240 12150 2745 12375 2700 12330 1980 11790 1575
+        11250 1935 10125 1485 8955 2070 7785 1620 7695 1575
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+        1.000
+4 0 0 100 0 0 12 0.0000 4 180 1440 10575 675 Ralf S. Engelschall\001
+4 0 0 100 0 18 20 0.0000 4 270 3840 4275 675 Apache+mod_ssl+OpenSSL\001
+4 0 0 100 0 0 10 0.0000 4 135 1320 10575 855 rse@engelschall.com\001
+4 0 0 100 0 0 10 0.0000 4 135 1410 10575 1035 www.engelschall.com\001
+4 0 0 100 0 0 12 0.0000 4 135 870 900 675 Version 1.3\001
+4 0 0 100 0 0 12 0.0000 4 180 1035 900 855 12-Apr-1999\001
+4 0 0 200 0 20 8 0.0000 4 60 390 6210 4680 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 855 8280 6120 ap_ctx_get(...,"ssl")\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 7740 2700 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 810 7740 2835 ->module_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 7740 2970 &ssl_module)\001
+4 0 0 100 0 18 20 0.0000 4 270 1200 9000 8100 Chaining\001
+4 0 0 100 0 18 20 0.0000 4 210 1095 2745 8100 Lifetime\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 810 6255 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 990 5805 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 840 4050 4455 SSL_CTX\001
+4 0 0 100 0 18 12 0.0000 4 150 975 4455 5355 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 3870 4905 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 1845 4005 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 150 810 2070 3555 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 345 2295 3105 BIO\001
+4 0 0 100 0 18 12 0.0000 4 135 375 2565 2655 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 3645 1620 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1065 3915 2070 request_rec\001
+4 0 0 200 0 0 8 0.0000 4 120 1440 900 7560 Startup, Runtime, Shutdown\001
+4 0 0 200 0 0 8 0.0000 4 105 975 1350 7335 Configuration Time\001
+4 0 0 200 0 0 8 0.0000 4 90 1050 2025 7110 Connection Duration\001
+4 0 0 200 0 0 8 0.0000 4 120 885 2835 6885 Request Duration\001
+4 0 0 200 0 18 20 0.0000 4 195 90 6345 6795 t\001
+4 0 0 200 0 20 8 0.0000 4 90 345 7110 5985 ->client\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 6795 2430 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 8865 2430 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 6345 3555 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 150 975 6345 4455 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 150 810 6345 5355 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 375 9720 5355 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 10665 2430 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 7290 6255 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 11025 5355 SSL_METHOD\001
+4 0 0 100 0 18 12 0.0000 4 180 840 11475 4455 SSL_CTX\001
+4 0 0 100 0 18 24 0.0000 4 285 4365 3915 1080 Data Structure Overview\001
+4 0 0 200 0 20 8 0.0000 4 90 615 7065 5085 ->connection\001
+4 0 0 200 0 20 8 0.0000 4 60 390 7065 4770 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 960 8010 5445 SSL_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 510 10530 4050 ->pSSLCtx\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 7875 4275 SSL_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1155 10305 5535 SSL_get_current_cipher()\001
+4 0 0 100 0 18 12 0.0000 4 180 1170 11025 5760 SSL_CIPHER\001
+4 0 0 100 0 18 12 0.0000 4 180 1350 10980 6165 SSL_SESSION\001
+4 0 0 200 0 20 8 0.0000 4 120 840 10440 5940 SSL_get_session()\001
+4 0 0 100 0 18 12 0.0000 4 180 1665 10575 7110 X509_STORE_CTX\001
+4 0 0 100 0 18 12 0.0000 4 135 345 9720 7110 BIO\001
+4 0 0 200 0 20 8 0.0000 4 120 840 9540 7335 SSL_get_{r,w}bio()\001
+4 0 0 100 0 18 20 0.0000 4 270 1170 8730 3465 mod_ssl\001
+4 0 0 100 0 18 20 0.0000 4 270 1050 8145 6750 Apache\001
+4 0 0 200 0 20 8 0.0000 4 120 945 10125 4680 SSL_get_SSL_CTX()\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 10350 5175 SSL_get_SSL_METHOD()\001
+4 0 0 200 0 20 8 0.0000 4 90 465 11745 4770 ->method\001
+4 0 0 200 0 20 8 0.0000 4 120 1665 9945 6480 X509_STORE_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 10980 6705 SSL_CTX_get_cert_store()\001
+4 0 0 200 0 20 8 0.0000 4 120 1020 8280 5130 SSL_get_app_data2()\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10710 7605 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 720 10710 7785 [Crypto]\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10935 3645 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 495 10935 3825 [SSL]\001
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/README.dsov.ps b/modules/http2/sandbox/httpd/mod_ssl-alpn/README.dsov.ps
new file mode 100644 (file)
index 0000000..def19db
--- /dev/null
@@ -0,0 +1,1138 @@
+%!PS-Adobe-2.0
+%%Title: README.dsov.ps
+%%Creator: fig2dev Version 3.2 Patchlevel 1
+%%CreationDate: Mon Apr 12 17:09:11 1999
+%%For: rse@en1.engelschall.com (Ralf S. Engelschall)
+%%Orientation: Landscape
+%%BoundingBox: 59 37 553 755
+%%Pages: 1
+%%BeginSetup
+%%IncludeFeature: *PageSize Letter
+%%EndSetup
+%%Magnification: 0.9340
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+/col32 {0.380 0.396 0.380 srgb} bind def
+/col33 {0.714 0.698 0.714 srgb} bind def
+/col34 {0.969 0.953 0.969 srgb} bind def
+/col35 {0.812 0.812 0.812 srgb} bind def
+/col36 {1.000 1.000 1.000 srgb} bind def
+
+end
+save
+48.0 12.0 translate
+ 90 rotate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add
+  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+  bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+  4 -2 roll mul srgb} bind def
+/reencdict 12 dict def /ReEncode { reencdict begin
+/newcodesandnames exch def /newfontname exch def /basefontname exch def
+/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def
+basefontdict { exch dup /FID ne { dup /Encoding eq
+{ exch dup length array copy newfont 3 1 roll put }
+{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall
+newfont /FontName newfontname put newcodesandnames aload pop
+128 1 255 { newfont /Encoding get exch /.notdef put } for
+newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat
+newfontname newfont definefont pop end } def
+/isovec [
+8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde
+8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis
+8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron
+8#220 /dotlessi 8#230 /oe 8#231 /OE
+8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling
+8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis
+8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot
+8#255 /endash 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus
+8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph
+8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine
+8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf
+8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute
+8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring
+8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute
+8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute
+8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve
+8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply
+8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex
+8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave
+8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring
+8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute
+8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute
+8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve
+8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide
+8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex
+8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def
+/Times-Roman /Times-Roman-iso isovec ReEncode
+/Helvetica-Bold /Helvetica-Bold-iso isovec ReEncode
+/Helvetica-Narrow /Helvetica-Narrow-iso isovec ReEncode
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+%%EndProlog
+
+$F2psBegin
+10 setmiterlimit
+n -1000 9572 m -1000 -1000 l 13622 -1000 l 13622 9572 l cp clip
+ 0.05883 0.05883 sc
+%%Page: 1 1
+% Polyline
+7.500 slw
+n 6413 2048 m 6380 2054 l 6348 2061 l 6315 2067 l 6283 2073 l 6250 2079 l
+ 6217 2084 l 6185 2090 l 6152 2095 l 6120 2101 l 6088 2107 l
+ 6057 2113 l 6027 2120 l 5998 2126 l 5970 2134 l 5943 2141 l
+ 5918 2149 l 5894 2158 l 5873 2167 l 5853 2177 l 5835 2187 l
+ 5819 2198 l 5805 2210 l 5793 2222 l 5782 2235 l 5774 2250 l
+ 5768 2265 l 5763 2281 l 5760 2299 l 5759 2318 l 5759 2339 l
+ 5761 2360 l 5764 2383 l 5768 2408 l 5774 2433 l 5780 2460 l
+ 5788 2488 l 5797 2516 l 5806 2546 l 5815 2575 l 5825 2606 l
+ 5836 2636 l 5846 2666 l 5856 2696 l 5866 2726 l 5875 2755 l
+ 5884 2784 l 5892 2812 l 5899 2839 l 5905 2866 l 5910 2891 l
+ 5915 2916 l 5918 2940 l 5919 2968 l 5920 2995 l 5919 3022 l
+ 5916 3048 l 5912 3075 l 5908 3101 l 5902 3127 l 5895 3153 l
+ 5887 3179 l 5880 3205 l 5871 3230 l 5863 3254 l 5855 3278 l
+ 5848 3302 l 5841 3324 l 5834 3346 l 5829 3367 l 5824 3388 l
+ 5821 3408 l 5819 3427 l 5819 3446 l 5820 3465 l 5823 3484 l
+ 5827 3503 l 5833 3522 l 5840 3542 l 5848 3562 l 5858 3582 l
+ 5868 3603 l 5880 3625 l 5891 3647 l 5904 3669 l 5916 3691 l
+ 5929 3713 l 5941 3736 l 5953 3758 l 5964 3779 l 5974 3801 l
+ 5983 3822 l 5991 3843 l 5997 3863 l 6002 3883 l 6006 3903 l
+ 6008 3923 l 6008 3942 l 6006 3962 l 6003 3983 l 5998 4004 l
+ 5992 4025 l 5985 4048 l 5977 4070 l 5968 4094 l 5958 4118 l
+ 5947 4142 l 5936 4167 l 5925 4192 l 5913 4216 l 5902 4241 l
+ 5892 4266 l 5882 4291 l 5872 4315 l 5864 4339 l 5857 4362 l
+ 5851 4386 l 5846 4409 l 5843 4433 l 5840 4456 l 5840 4480 l
+ 5840 4505 l 5842 4530 l 5845 4556 l 5849 4582 l 5854 4609 l
+ 5860 4636 l 5867 4664 l 5875 4692 l 5883 4720 l 5892 4747 l
+ 5901 4774 l 5910 4801 l 5920 4827 l 5929 4852 l 5938 4875 l
+ 5947 4898 l 5955 4920 l 5963 4941 l 5971 4961 l 5978 4980 l
+ 5985 5002 l 5992 5024 l 5999 5046 l 6005 5067 l 6010 5088 l
+ 6016 5109 l 6022 5129 l 6027 5150 l 6033 5170 l 6039 5190 l
+ 6045 5209 l 6052 5228 l 6059 5246 l 6067 5264 l 6075 5281 l
+ 6084 5298 l 6094 5315 l 6105 5333 l 6115 5347 l 6125 5361 l
+ 6137 5376 l 6149 5392 l 6162 5408 l 6176 5425 l 6191 5443 l
+ 6206 5461 l 6221 5480 l 6237 5499 l 6253 5519 l 6269 5539 l
+ 6284 5559 l 6299 5579 l 6313 5599 l 6327 5619 l 6340 5639 l
+ 6352 5659 l 6363 5679 l 6373 5698 l 6382 5718 l 6390 5738 l
+ 6398 5759 l 6404 5782 l 6410 5805 l 6415 5828 l 6420 5852 l
+ 6424 5877 l 6428 5902 l 6431 5927 l 6435 5952 l 6438 5977 l
+ 6442 6001 l 6446 6025 l 6450 6048 l 6455 6069 l 6461 6090 l
+ 6467 6109 l 6474 6127 l 6483 6143 l 6492 6159 l 6503 6173 l
+ 6515 6185 l 6528 6197 l 6543 6209 l 6560 6220 l 6578 6230 l
+ 6598 6240 l 6619 6250 l 6641 6260 l 6663 6270 l 6687 6281 l
+ 6710 6291 l 6733 6302 l 6757 6312 l 6779 6324 l 6801 6335 l
+ 6821 6348 l 6841 6361 l 6859 6374 l 6876 6389 l 6893 6405 l
+ 6906 6421 l 6919 6437 l 6932 6455 l 6944 6475 l 6955 6495 l
+ 6967 6516 l 6979 6538 l 6991 6561 l 7003 6584 l 7015 6608 l
+ 7027 6631 l 7040 6654 l 7053 6677 l 7067 6699 l 7081 6720 l
+ 7096 6739 l 7111 6758 l 7127 6774 l 7144 6789 l 7161 6803 l
+ 7180 6815 l 7200 6825 l 7220 6833 l 7240 6840 l 7263 6845 l
+ 7286 6850 l 7311 6854 l 7338 6857 l 7365 6859 l 7394 6861 l
+ 7424 6862 l 7454 6864 l 7485 6865 l 7516 6866 l 7547 6867 l
+ 7578 6868 l 7609 6870 l 7639 6872 l 7668 6875 l 7696 6879 l
+ 7723 6883 l 7748 6889 l 7773 6895 l 7795 6903 l 7817 6912 l
+ 7838 6923 l 7857 6934 l 7875 6948 l 7892 6963 l 7909 6980 l
+ 7926 6998 l 7941 7017 l 7957 7038 l 7972 7060 l 7987 7083 l
+ 8002 7106 l 8017 7130 l 8031 7154 l 8046 7178 l 8061 7202 l
+ 8075 7225 l 8090 7247 l 8105 7269 l 8120 7289 l 8135 7308 l
+ 8151 7326 l 8167 7342 l 8184 7356 l 8202 7369 l 8220 7380 l
+ 8239 7390 l 8260 7397 l 8282 7404 l 8305 7409 l 8330 7413 l
+ 8356 7416 l 8383 7418 l 8412 7420 l 8441 7420 l 8471 7419 l
+ 8502 7418 l 8534 7417 l 8565 7415 l 8597 7413 l 8629 7411 l
+ 8660 7409 l 8690 7407 l 8720 7405 l 8749 7404 l 8777 7404 l
+ 8804 7404 l 8830 7405 l 8856 7407 l 8880 7410 l 8906 7414 l
+ 8931 7420 l 8956 7427 l 8981 7435 l 9005 7444 l 9029 7455 l
+ 9053 7466 l 9077 7478 l 9100 7491 l 9123 7504 l 9146 7517 l
+ 9168 7531 l 9190 7544 l 9210 7557 l 9230 7570 l 9250 7582 l
+ 9268 7593 l 9286 7604 l 9304 7613 l 9320 7621 l 9336 7629 l
+ 9353 7635 l 9370 7641 l 9388 7645 l 9406 7648 l 9425 7650 l
+ 9444 7652 l 9464 7653 l 9485 7653 l 9508 7653 l 9531 7653 l
+ 9555 7653 l 9579 7653 l 9605 7654 l 9631 7655 l 9658 7656 l
+ 9685 7659 l 9713 7662 l 9742 7666 l 9771 7672 l 9801 7679 l
+ 9833 7688 l 9853 7694 l 9874 7700 l 9895 7708 l 9918 7716 l
+ 9941 7725 l 9966 7734 l 9991 7745 l 10017 7755 l 10045 7767 l
+ 10073 7779 l 10102 7791 l 10132 7804 l 10163 7818 l 10194 7831 l
+ 10227 7845 l 10259 7860 l 10293 7874 l 10326 7889 l 10360 7903 l
+ 10394 7918 l 10429 7932 l 10463 7947 l 10497 7961 l 10531 7974 l
+ 10565 7988 l 10599 8001 l 10633 8013 l 10667 8025 l 10700 8037 l
+ 10733 8049 l 10767 8059 l 10800 8070 l 10834 8080 l 10868 8090 l
+ 10902 8099 l 10937 8108 l 10973 8117 l 11009 8125 l 11045 8133 l
+ 11083 8141 l 11120 8148 l 11158 8155 l 11197 8161 l 11236 8167 l
+ 11275 8172 l 11313 8177 l 11352 8181 l 11391 8184 l 11429 8187 l
+ 11467 8190 l 11504 8191 l 11540 8192 l 11576 8192 l 11610 8192 l
+ 11644 8191 l 11676 8189 l 11707 8187 l 11738 8184 l 11767 8180 l
+ 11794 8176 l 11821 8171 l 11847 8165 l 11871 8159 l 11895 8153 l
+ 11923 8143 l 11950 8133 l 11976 8122 l 12001 8109 l 12025 8096 l
+ 12048 8081 l 12071 8065 l 12092 8048 l 12113 8031 l 12133 8012 l
+ 12153 7992 l 12171 7972 l 12188 7951 l 12205 7930 l 12220 7909 l
+ 12235 7887 l 12248 7865 l 12260 7843 l 12272 7822 l 12282 7800 l
+ 12292 7779 l 12301 7759 l 12309 7739 l 12316 7719 l 12323 7699 l
+ 12330 7680 l 12338 7655 l 12345 7631 l 12352 7607 l 12359 7582 l
+ 12365 7558 l 12371 7533 l 12377 7508 l 12382 7484 l 12388 7460 l
+ 12392 7436 l 12397 7414 l 12401 7391 l 12405 7370 l 12409 7350 l
+ 12412 7331 l 12415 7313 l 12418 7297 l 12421 7281 l 12424 7266 l
+ 12428 7253 l 12432 7234 l 12437 7216 l 12442 7199 l 12446 7183 l
+ 12451 7166 l 12456 7150 l 12460 7134 l 12463 7117 l 12466 7101 l
+ 12468 7086 l 12469 7070 l 12469 7054 l 12467 7037 l 12465 7020 l
+ 12462 7006 l 12459 6991 l 12455 6975 l 12450 6958 l 12445 6940 l
+ 12440 6921 l 12434 6901 l 12428 6880 l 12422 6859 l 12416 6838 l
+ 12411 6817 l 12406 6796 l 12401 6776 l 12397 6756 l 12394 6736 l
+ 12392 6718 l 12390 6700 l 12390 6683 l 12390 6665 l 12392 6649 l
+ 12394 6631 l 12397 6614 l 12401 6597 l 12406 6579 l 12411 6561 l
+ 12416 6542 l 12422 6524 l 12428 6505 l 12434 6487 l 12440 6468 l
+ 12445 6450 l 12450 6432 l 12455 6414 l 12459 6396 l 12462 6378 l
+ 12465 6360 l 12467 6343 l 12468 6326 l 12469 6308 l 12469 6289 l
+ 12468 6269 l 12468 6249 l 12466 6227 l 12464 6205 l 12462 6182 l
+ 12460 6159 l 12457 6135 l 12454 6111 l 12451 6087 l 12447 6063 l
+ 12444 6040 l 12441 6016 l 12437 5993 l 12434 5970 l 12431 5948 l
+ 12428 5925 l 12424 5902 l 12421 5879 l 12419 5855 l 12416 5831 l
+ 12413 5806 l 12411 5781 l 12408 5755 l 12406 5729 l 12404 5702 l
+ 12403 5676 l 12401 5651 l 12400 5625 l 12400 5601 l 12399 5578 l
+ 12399 5555 l 12400 5534 l 12401 5514 l 12402 5495 l 12403 5477 l
+ 12405 5460 l 12408 5440 l 12411 5421 l 12416 5402 l 12420 5384 l
+ 12426 5365 l 12431 5347 l 12437 5329 l 12444 5311 l 12450 5293 l
+ 12456 5275 l 12462 5258 l 12468 5240 l 12474 5222 l 12479 5205 l
+ 12483 5186 l 12488 5168 l 12490 5152 l 12493 5135 l 12496 5117 l
+ 12498 5099 l 12500 5079 l 12502 5058 l 12504 5036 l 12506 5014 l
+ 12507 4990 l 12509 4966 l 12510 4942 l 12512 4918 l 12513 4893 l
+ 12515 4869 l 12516 4845 l 12518 4822 l 12520 4799 l 12521 4776 l
+ 12523 4754 l 12525 4733 l 12527 4713 l 12529 4693 l 12531 4673 l
+ 12534 4653 l 12536 4632 l 12539 4610 l 12541 4588 l 12543 4566 l
+ 12546 4543 l 12548 4520 l 12550 4497 l 12552 4473 l 12553 4450 l
+ 12554 4426 l 12555 4403 l 12555 4380 l 12555 4357 l 12555 4334 l
+ 12554 4312 l 12552 4290 l 12550 4267 l 12548 4245 l 12545 4224 l
+ 12541 4203 l 12537 4181 l 12533 4159 l 12528 4136 l 12523 4112 l
+ 12517 4088 l 12510 4064 l 12503 4038 l 12496 4013 l 12488 3987 l
+ 12479 3961 l 12471 3935 l 12462 3909 l 12452 3884 l 12443 3859 l
+ 12434 3835 l 12424 3811 l 12415 3788 l 12405 3766 l 12396 3744 l
+ 12386 3723 l 12377 3702 l 12368 3683 l 12357 3661 l 12347 3640 l
+ 12336 3619 l 12325 3598 l 12314 3576 l 12303 3555 l 12291 3533 l
+ 12280 3511 l 12269 3489 l 12257 3467 l 12246 3446 l 12235 3424 l
+ 12225 3402 l 12215 3381 l 12206 3360 l 12197 3340 l 12189 3320 l
+ 12181 3301 l 12174 3281 l 12168 3262 l 12162 3244 l 12158 3225 l
+ 12153 3204 l 12149 3183 l 12145 3162 l 12142 3139 l 12140 3117 l
+ 12138 3094 l 12137 3071 l 12137 3047 l 12138 3024 l 12139 3001 l
+ 12141 2978 l 12143 2956 l 12146 2935 l 12150 2915 l 12154 2896 l
+ 12158 2879 l 12163 2862 l 12168 2847 l 12174 2833 l 12180 2820 l
+ 12188 2805 l 12197 2792 l 12206 2779 l 12216 2766 l 12227 2754 l
+ 12238 2742 l 12249 2730 l 12260 2717 l 12272 2704 l 12282 2691 l
+ 12292 2676 l 12302 2661 l 12310 2645 l 12318 2627 l 12324 2608 l
+ 12330 2588 l 12334 2571 l 12336 2553 l 12339 2534 l 12341 2513 l
+ 12342 2491 l 12343 2467 l 12343 2442 l 12342 2416 l 12340 2389 l
+ 12338 2360 l 12335 2332 l 12331 2303 l 12326 2273 l 12320 2244 l
+ 12314 2215 l 12307 2187 l 12299 2159 l 12290 2132 l 12280 2106 l
+ 12270 2081 l 12259 2056 l 12248 2033 l 12236 2011 l 12224 1990 l
+ 12210 1970 l 12196 1949 l 12181 1929 l 12164 1910 l 12147 1890 l
+ 12129 1871 l 12110 1853 l 12090 1835 l 12070 1818 l 12049 1802 l
+ 12027 1787 l 12005 1773 l 11983 1761 l 11961 1749 l 11939 1739 l
+ 11917 1730 l 11895 1722 l 11874 1716 l 11852 1710 l 11831 1707 l
+ 11811 1704 l 11790 1703 l 11769 1702 l 11748 1703 l 11727 1705 l
+ 11706 1708 l 11683 1711 l 11660 1716 l 11636 1721 l 11612 1727 l
+ 11587 1733 l 11560 1740 l 11534 1747 l 11506 1754 l 11479 1761 l
+ 11450 1768 l 11422 1774 l 11393 1780 l 11364 1786 l 11334 1791 l
+ 11305 1795 l 11275 1798 l 11245 1800 l 11215 1801 l 11184 1801 l
+ 11153 1800 l 11128 1798 l 11104 1796 l 11078 1793 l 11052 1790 l
+ 11025 1785 l 10997 1781 l 10968 1776 l 10939 1770 l 10908 1764 l
+ 10877 1758 l 10844 1751 l 10811 1744 l 10778 1737 l 10743 1730 l
+ 10708 1722 l 10673 1715 l 10637 1708 l 10601 1701 l 10565 1695 l
+ 10530 1688 l 10494 1682 l 10458 1677 l 10422 1672 l 10387 1668 l
+ 10352 1664 l 10318 1661 l 10284 1658 l 10250 1657 l 10216 1656 l
+ 10183 1655 l 10150 1656 l 10118 1658 l 10087 1660 l 10055 1663 l
+ 10024 1666 l 9992 1671 l 9960 1676 l 9927 1682 l 9894 1688 l
+ 9861 1695 l 9827 1703 l 9792 1711 l 9757 1720 l 9721 1729 l
+ 9685 1738 l 9649 1748 l 9613 1757 l 9576 1767 l 9539 1778 l
+ 9502 1788 l 9465 1798 l 9429 1807 l 9392 1817 l 9356 1826 l
+ 9320 1835 l 9285 1844 l 9250 1852 l 9216 1860 l 9182 1867 l
+ 9148 1873 l 9115 1879 l 9082 1884 l 9050 1889 l 9018 1892 l
+ 8987 1895 l 8955 1898 l 8919 1899 l 8883 1900 l 8847 1899 l
+ 8811 1898 l 8774 1896 l 8737 1893 l 8699 1889 l 8661 1884 l
+ 8623 1878 l 8585 1872 l 8546 1865 l 8508 1857 l 8470 1849 l
+ 8432 1840 l 8395 1830 l 8358 1821 l 8322 1811 l 8287 1801 l
+ 8254 1790 l 8221 1780 l 8189 1770 l 8159 1760 l 8130 1750 l
+ 8102 1740 l 8076 1730 l 8051 1721 l 8028 1712 l 8006 1703 l
+ 7985 1695 l 7965 1688 l 7931 1674 l 7899 1662 l 7871 1650 l
+ 7844 1640 l 7820 1631 l 7798 1623 l 7778 1617 l 7760 1611 l
+ 7743 1607 l 7728 1603 l 7715 1601 l 7702 1600 l 7691 1600 l
+ 7680 1601 l 7669 1603 l 7658 1605 l 7648 1607 l 7638 1610 l
+ 7627 1613 l 7615 1617 l 7601 1621 l 7587 1626 l 7571 1632 l
+ 7554 1638 l 7536 1645 l 7517 1653 l 7496 1661 l 7474 1670 l
+ 7452 1679 l 7428 1689 l 7403 1699 l 7378 1709 l 7352 1720 l
+ 7325 1731 l 7297 1743 l 7268 1755 l 7247 1763 l 7226 1772 l
+ 7204 1781 l 7182 1790 l 7158 1800 l 7133 1810 l 7108 1820 l
+ 7081 1831 l 7053 1842 l 7025 1853 l 6996 1864 l 6966 1875 l
+ 6935 1886 l 6904 1898 l 6873 1909 l 6841 1921 l 6809 1932 l
+ 6776 1943 l 6744 1954 l 6712 1964 l 6680 1974 l 6649 1984 l
+ 6618 1994 l 6587 2003 l 6557 2011 l 6527 2019 l 6498 2027 l
+ 6469 2034 l 6441 2041 l cp gs col34 1.00 shd ef gr gs col34 s gr 
+% Polyline
+n 675 6525 m 5850 6525 l 5850 6075 l 5625 6075 l 5625 5625 l 900 5625 l
+ 900 6075 l 675 6075 l cp gs col7 1.00 shd ef gr gs col7 s gr 
+% Polyline
+n 1125 6525 m 5355 6525 l 5400 5175 l 5175 5175 l 5175 4725 l 4950 4725 l
+ 4950 4275 l 1575 4275 l 1575 4725 l 1350 4725 l 1350 5175 l
+ 1125 5175 l cp gs col34 1.00 shd ef gr gs col34 s gr 
+% Polyline
+75.000 slw
+n 9450 4500 m 12465 2205 l gs col7 s gr 
+% Polyline
+n 9450 4500 m 9450 7785 l gs col7 s gr 
+% Polyline
+n 9450 4500 m 6075 1935 l gs col7 s gr 
+% Polyline
+n 12510 6435 m 9450 6435 l gs col7 s gr 
+% Polyline
+7.500 slw
+n 1800 6525 m 4725 6525 l 4725 3825 l 4500 3825 l 4500 3375 l 4275 3375 l
+ 4275 2925 l 4050 2925 l 4050 2475 l 2475 2475 l 2475 2925 l
+ 2250 2925 l 2250 3375 l 2025 3375 l 2025 3825 l 1800 3825 l
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 2700 6525 m 3825 6525 l 3825 2025 l 3600 2025 l 3600 1575 l 2925 1575 l
+ 2925 2025 l 2700 2025 l cp gs col33 1.00 shd ef gr gs col33 s gr 
+% Polyline
+gs  clippath
+12068 6810 m 11970 6885 l 12022 6773 l 11937 6878 l 11984 6915 l cp
+clip
+n 12375 4455 m 12510 4635 l 12510 6210 l 11970 6885 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 12068 6810 m 11970 6885 l 12022 6773 l 12045 6791 l 12068 6810 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7113 6004 m 7155 6120 l 7063 6037 l 7138 6149 l 7188 6116 l cp
+clip
+n 6705 5445 m 7155 6120 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7113 6004 m 7155 6120 l 7063 6037 l 7088 6020 l 7113 6004 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7304 4656 m 7200 4590 l 7323 4599 l 7195 4557 l 7176 4614 l cp
+clip
+n 7875 4815 m 7200 4590 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7304 4656 m 7200 4590 l 7323 4599 l 7314 4628 l 7304 4656 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+11405 4128 m 11475 4230 l 11365 4173 l 11466 4262 l 11506 4217 l cp
+clip
+n 9585 2565 m 11475 4230 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11405 4128 m 11475 4230 l 11365 4173 l 11385 4151 l 11405 4128 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+11712 4556 m 11835 4545 l 11732 4613 l 11859 4568 l 11839 4512 l cp
+clip
+n 10170 5130 m 11835 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11712 4556 m 11835 4545 l 11732 4613 l 11722 4585 l 11712 4556 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+9732 5411 m 9855 5400 l 9752 5468 l 9879 5423 l 9859 5367 l cp
+clip
+n 7920 6075 m 9855 5400 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9732 5411 m 9855 5400 l 9752 5468 l 9742 5440 l 9732 5411 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10823 5573 m 10935 5625 l 10812 5632 l 10944 5657 l 10955 5598 l cp
+clip
+n 9990 5445 m 10935 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10823 5573 m 10935 5625 l 10812 5632 l 10817 5603 l 10823 5573 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10815 5280 m 10935 5310 l 10815 5340 l 10950 5340 l 10950 5280 l cp
+clip
+n 10215 5310 m 10935 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10815 5280 m 10935 5310 l 10815 5340 l 10815 5310 l 10815 5280 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+11955 4965 m 11925 5085 l 11895 4965 l 11895 5100 l 11955 5100 l cp
+clip
+n 11925 4590 m 11925 5085 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11955 4965 m 11925 5085 l 11895 4965 l 11925 4965 l 11955 4965 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+9840 6720 m 9810 6840 l 9780 6720 l 9780 6855 l 9840 6855 l cp
+clip
+n 9810 5490 m 9810 6840 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9840 6720 m 9810 6840 l 9780 6720 l 9810 6720 l 9840 6720 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10847 5943 m 10935 6030 l 10816 5995 l 10933 6063 l 10963 6012 l cp
+clip
+n 9945 5445 m 10935 6030 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10847 5943 m 10935 6030 l 10816 5995 l 10832 5969 l 10847 5943 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+10698 2634 m 10800 2565 l 10742 2674 l 10832 2574 l 10788 2534 l cp
+clip
+n 8865 4725 m 10800 2565 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10698 2634 m 10800 2565 l 10742 2674 l 10720 2654 l 10698 2634 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+30.000 slw
+n 675 6075 m 5850 6075 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs  clippath
+645 6195 m 675 6075 l 705 6195 l 705 6060 l 645 6060 l cp
+clip
+n 675 6525 m 675 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 645 6195 m 675 6075 l 705 6195 l 675 6195 l 645 6195 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5880 6405 m 5850 6525 l 5820 6405 l 5820 6540 l 5880 6540 l cp
+clip
+n 5850 6075 m 5850 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5880 6405 m 5850 6525 l 5820 6405 l 5850 6405 l 5880 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+30.000 slw
+n 900 5625 m 5625 5625 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1125 5175 m 5400 5175 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1350 4725 m 5175 4725 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1575 4275 m 4950 4275 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 1800 3825 m 4725 3825 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2025 3375 m 4500 3375 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2250 2925 m 4275 2925 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2475 2475 m 4050 2475 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2700 2025 m 3825 2025 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 2925 1575 m 3600 1575 l gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs  clippath
+870 5745 m 900 5625 l 930 5745 l 930 5610 l 870 5610 l cp
+clip
+n 900 6075 m 900 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 870 5745 m 900 5625 l 930 5745 l 900 5745 l 870 5745 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1095 5295 m 1125 5175 l 1155 5295 l 1155 5160 l 1095 5160 l cp
+clip
+n 1125 6525 m 1125 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1095 5295 m 1125 5175 l 1155 5295 l 1125 5295 l 1095 5295 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1320 4845 m 1350 4725 l 1380 4845 l 1380 4710 l 1320 4710 l cp
+clip
+n 1350 5175 m 1350 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1320 4845 m 1350 4725 l 1380 4845 l 1350 4845 l 1320 4845 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1545 4395 m 1575 4275 l 1605 4395 l 1605 4260 l 1545 4260 l cp
+clip
+n 1575 4725 m 1575 4275 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1545 4395 m 1575 4275 l 1605 4395 l 1575 4395 l 1545 4395 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1770 3945 m 1800 3825 l 1830 3945 l 1830 3810 l 1770 3810 l cp
+clip
+n 1800 6525 m 1800 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1770 3945 m 1800 3825 l 1830 3945 l 1800 3945 l 1770 3945 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+1995 3495 m 2025 3375 l 2055 3495 l 2055 3360 l 1995 3360 l cp
+clip
+n 2025 3825 m 2025 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1995 3495 m 2025 3375 l 2055 3495 l 2025 3495 l 1995 3495 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2220 3045 m 2250 2925 l 2280 3045 l 2280 2910 l 2220 2910 l cp
+clip
+n 2250 3375 m 2250 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2220 3045 m 2250 2925 l 2280 3045 l 2250 3045 l 2220 3045 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2445 2595 m 2475 2475 l 2505 2595 l 2505 2460 l 2445 2460 l cp
+clip
+n 2475 2925 m 2475 2475 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2445 2595 m 2475 2475 l 2505 2595 l 2475 2595 l 2445 2595 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5655 5955 m 5625 6075 l 5595 5955 l 5595 6090 l 5655 6090 l cp
+clip
+n 5625 5625 m 5625 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5655 5955 m 5625 6075 l 5595 5955 l 5625 5955 l 5655 5955 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5430 6405 m 5400 6525 l 5370 6405 l 5370 6540 l 5430 6540 l cp
+clip
+n 5400 5175 m 5400 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5430 6405 m 5400 6525 l 5370 6405 l 5400 6405 l 5430 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+5205 5055 m 5175 5175 l 5145 5055 l 5145 5190 l 5205 5190 l cp
+clip
+n 5175 4725 m 5175 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5205 5055 m 5175 5175 l 5145 5055 l 5175 5055 l 5205 5055 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4980 4605 m 4950 4725 l 4920 4605 l 4920 4740 l 4980 4740 l cp
+clip
+n 4950 4275 m 4950 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4980 4605 m 4950 4725 l 4920 4605 l 4950 4605 l 4980 4605 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4755 6405 m 4725 6525 l 4695 6405 l 4695 6540 l 4755 6540 l cp
+clip
+n 4725 3825 m 4725 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4755 6405 m 4725 6525 l 4695 6405 l 4725 6405 l 4755 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4530 3705 m 4500 3825 l 4470 3705 l 4470 3840 l 4530 3840 l cp
+clip
+n 4500 3375 m 4500 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4530 3705 m 4500 3825 l 4470 3705 l 4500 3705 l 4530 3705 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4305 3255 m 4275 3375 l 4245 3255 l 4245 3390 l 4305 3390 l cp
+clip
+n 4275 2925 m 4275 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4305 3255 m 4275 3375 l 4245 3255 l 4275 3255 l 4305 3255 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+4080 2805 m 4050 2925 l 4020 2805 l 4020 2940 l 4080 2940 l cp
+clip
+n 4050 2475 m 4050 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4080 2805 m 4050 2925 l 4020 2805 l 4050 2805 l 4080 2805 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2670 2145 m 2700 2025 l 2730 2145 l 2730 2010 l 2670 2010 l cp
+clip
+n 2700 6525 m 2700 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2670 2145 m 2700 2025 l 2730 2145 l 2700 2145 l 2670 2145 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+3855 6405 m 3825 6525 l 3795 6405 l 3795 6540 l 3855 6540 l cp
+clip
+n 3825 2025 m 3825 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3855 6405 m 3825 6525 l 3795 6405 l 3825 6405 l 3855 6405 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+3630 1905 m 3600 2025 l 3570 1905 l 3570 2040 l 3630 2040 l cp
+clip
+n 3600 1575 m 3600 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3630 1905 m 3600 2025 l 3570 1905 l 3600 1905 l 3630 1905 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+2895 1695 m 2925 1575 l 2955 1695 l 2955 1560 l 2895 1560 l cp
+clip
+n 2925 2025 m 2925 1575 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2895 1695 m 2925 1575 l 2955 1695 l 2925 1695 l 2895 1695 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+45.000 slw
+gs  clippath
+6087 6495 m 6207 6525 l 6087 6555 l 6360 6555 l 6360 6495 l cp
+clip
+n 540 6525 m 6300 6525 l gs 0.00 setgray ef gr gs col0 s gr gr
+
+% arrowhead
+n 6087 6495 m 6207 6525 l 6087 6555 l 6087 6525 l 6087 6495 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+7.500 slw
+gs  clippath
+3681 6720 m 3825 6750 l 3681 6780 l 3840 6780 l 3840 6720 l cp
+2844 6780 m 2700 6750 l 2844 6720 l 2685 6720 l 2685 6780 l cp
+clip
+n 2700 6750 m 3825 6750 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 2844 6780 m 2700 6750 l 2844 6720 l 2820 6750 l 2844 6780 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 3681 6720 m 3825 6750 l 3681 6780 l 3705 6750 l 3681 6720 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+gs  clippath
+5256 7170 m 5400 7200 l 5256 7230 l 5415 7230 l 5415 7170 l cp
+1269 7230 m 1125 7200 l 1269 7170 l 1110 7170 l 1110 7230 l cp
+clip
+n 1125 7200 m 5400 7200 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1269 7230 m 1125 7200 l 1269 7170 l 1245 7200 l 1269 7230 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 5256 7170 m 5400 7200 l 5256 7230 l 5280 7200 l 5256 7170 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+gs  clippath
+4581 6945 m 4725 6975 l 4581 7005 l 4740 7005 l 4740 6945 l cp
+1944 7005 m 1800 6975 l 1944 6945 l 1785 6945 l 1785 7005 l cp
+clip
+n 1800 6975 m 4725 6975 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1944 7005 m 1800 6975 l 1944 6945 l 1920 6975 l 1944 7005 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 4581 6945 m 4725 6975 l 4581 7005 l 4605 6975 l 4581 6945 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+gs  clippath
+5706 7395 m 5850 7425 l 5706 7455 l 5865 7455 l 5865 7395 l cp
+819 7455 m 675 7425 l 819 7395 l 660 7395 l 660 7455 l cp
+clip
+n 675 7425 m 5850 7425 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 819 7455 m 675 7425 l 819 7395 l 795 7425 l 819 7455 l  cp gs col7 1.00 shd ef gr  col0 s
+% arrowhead
+n 5706 7395 m 5850 7425 l 5706 7455 l 5730 7425 l 5706 7395 l  cp gs col7 1.00 shd ef gr  col0 s
+% Polyline
+1 slc
+ [15 45] 45 sd
+n 675 6570 m 675 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1125 6570 m 1125 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1800 6570 m 1800 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 2700 6570 m 2700 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 3825 6570 m 3825 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 4725 6570 m 4725 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5400 6570 m 5400 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5850 6570 m 5850 7650 l gs col34 1.00 shd ef gr gs col0 s gr  [] 0 sd
+% Polyline
+0 slc
+n 750 225 m 450 225 450 1050 300 arcto 4 {pop} repeat
+  450 1350 12300 1350 300 arcto 4 {pop} repeat
+  12600 1350 12600 525 300 arcto 4 {pop} repeat
+  12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col34 1.00 shd ef gr gs col0 s gr 
+% Polyline
+n 8835 2250 m 8775 2250 8775 2415 60 arcto 4 {pop} repeat
+  8775 2475 10110 2475 60 arcto 4 {pop} repeat
+  10170 2475 10170 2310 60 arcto 4 {pop} repeat
+  10170 2250 8835 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 10635 2250 m 10575 2250 10575 2415 60 arcto 4 {pop} repeat
+  10575 2475 11865 2475 60 arcto 4 {pop} repeat
+  11925 2475 11925 2310 60 arcto 4 {pop} repeat
+  11925 2250 10635 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 11490 4275 m 11430 4275 11430 4440 60 arcto 4 {pop} repeat
+  11430 4500 12315 4500 60 arcto 4 {pop} repeat
+  12375 4500 12375 4335 60 arcto 4 {pop} repeat
+  12375 4275 11490 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 11040 5175 m 10980 5175 10980 5340 60 arcto 4 {pop} repeat
+  10980 5400 12315 5400 60 arcto 4 {pop} repeat
+  12375 5400 12375 5235 60 arcto 4 {pop} repeat
+  12375 5175 11040 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 9735 5175 m 9675 5175 9675 5340 60 arcto 4 {pop} repeat
+  9675 5400 10110 5400 60 arcto 4 {pop} repeat
+  10170 5400 10170 5235 60 arcto 4 {pop} repeat
+  10170 5175 9735 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 7260 6075 m 7200 6075 7200 6240 60 arcto 4 {pop} repeat
+  7200 6300 7815 6300 60 arcto 4 {pop} repeat
+  7875 6300 7875 6135 60 arcto 4 {pop} repeat
+  7875 6075 7260 6075 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6810 2250 m 6750 2250 6750 2415 60 arcto 4 {pop} repeat
+  6750 2475 8130 2475 60 arcto 4 {pop} repeat
+  8190 2475 8190 2310 60 arcto 4 {pop} repeat
+  8190 2250 6810 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6360 3375 m 6300 3375 6300 3540 60 arcto 4 {pop} repeat
+  6300 3600 7545 3600 60 arcto 4 {pop} repeat
+  7605 3600 7605 3435 60 arcto 4 {pop} repeat
+  7605 3375 6360 3375 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6360 4275 m 6300 4275 6300 4440 60 arcto 4 {pop} repeat
+  6300 4500 7275 4500 60 arcto 4 {pop} repeat
+  7335 4500 7335 4335 60 arcto 4 {pop} repeat
+  7335 4275 6360 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 6360 5175 m 6300 5175 6300 5340 60 arcto 4 {pop} repeat
+  6300 5400 7140 5400 60 arcto 4 {pop} repeat
+  7200 5400 7200 5235 60 arcto 4 {pop} repeat
+  7200 5175 6360 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+gs  clippath
+7365 5340 m 7245 5310 l 7365 5280 l 7230 5280 l 7230 5340 l cp
+clip
+n 9630 5310 m 7245 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7365 5340 m 7245 5310 l 7365 5280 l 7365 5310 l 7365 5340 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7500 4395 m 7380 4365 l 7500 4335 l 7365 4335 l 7365 4395 l cp
+clip
+n 11385 4365 m 7380 4365 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7500 4395 m 7380 4365 l 7500 4335 l 7500 4365 l 7500 4395 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+n 11040 5580 m 10980 5580 10980 5745 60 arcto 4 {pop} repeat
+  10980 5805 12180 5805 60 arcto 4 {pop} repeat
+  12240 5805 12240 5640 60 arcto 4 {pop} repeat
+  12240 5580 11040 5580 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 11040 5985 m 10980 5985 10980 6150 60 arcto 4 {pop} repeat
+  10980 6210 12315 6210 60 arcto 4 {pop} repeat
+  12375 6210 12375 6045 60 arcto 4 {pop} repeat
+  12375 5985 11040 5985 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+gs  clippath
+9958 5554 m 9900 5445 l 10003 5514 l 9912 5414 l 9868 5454 l cp
+clip
+n 11205 6885 m 9900 5445 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9958 5554 m 9900 5445 l 10003 5514 l 9981 5534 l 9958 5554 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+n 10590 6930 m 10530 6930 10530 7095 60 arcto 4 {pop} repeat
+  10530 7155 12225 7155 60 arcto 4 {pop} repeat
+  12285 7155 12285 6990 60 arcto 4 {pop} repeat
+  12285 6930 10590 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+n 9690 6930 m 9630 6930 9630 7095 60 arcto 4 {pop} repeat
+  9630 7155 10110 7155 60 arcto 4 {pop} repeat
+  10170 7155 10170 6990 60 arcto 4 {pop} repeat
+  10170 6930 9690 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+/Times-Roman-iso ff 120.00 scf sf
+900 7560 m
+gs 1 -1 sc (Startup, Runtime, Shutdown) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 2970 m
+gs 1 -1 sc (ap_ctx_get\(...,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2745 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2880 m
+gs 1 -1 sc (->per_dir_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 3015 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+% Polyline
+n 7980 4770 m 7920 4770 7920 4935 60 arcto 4 {pop} repeat
+  7920 4995 9075 4995 60 arcto 4 {pop} repeat
+  9135 4995 9135 4830 60 arcto 4 {pop} repeat
+  9135 4770 7980 4770 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr 
+% Polyline
+gs  clippath
+7340 2610 m 7425 2520 l 7393 2639 l 7459 2521 l 7406 2492 l cp
+clip
+n 6975 3330 m 7425 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7340 2610 m 7425 2520 l 7393 2639 l 7367 2625 l 7340 2610 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+9336 2569 m 9450 2520 l 9373 2616 l 9480 2535 l 9444 2487 l cp
+clip
+n 7200 4230 m 9450 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9336 2569 m 9450 2520 l 9373 2616 l 9354 2593 l 9336 2569 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+7321 5196 m 7200 5220 l 7296 5142 l 7174 5199 l 7199 5254 l cp
+clip
+n 7875 4905 m 7200 5220 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7321 5196 m 7200 5220 l 7296 5142 l 7309 5169 l 7321 5196 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+gs  clippath
+6720 4665 m 6750 4545 l 6780 4665 l 6780 4530 l 6720 4530 l cp
+clip
+n 6750 5130 m 6750 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 6720 4665 m 6750 4545 l 6780 4665 l 6750 4665 l 6720 4665 l  cp gs 0.00 setgray ef gr  col0 s
+% Polyline
+ [15 15] 15 sd
+gs  clippath
+9279 4984 m 9175 4918 l 9298 4927 l 9170 4885 l 9151 4942 l cp
+clip
+n 9850 5143 m 9175 4918 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 9279 4984 m 9175 4918 l 9298 4927 l 9289 4956 l 9279 4984 l  cp gs 0.00 setgray ef gr  col0 s
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6210 4680 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 6120 m
+gs 1 -1 sc (ap_ctx_get\(...,"ssl"\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2700 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2835 m
+gs 1 -1 sc (->module_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2970 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 3105 m
+gs 1 -1 sc ("ssl_module"\)) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+1350 7335 m
+gs 1 -1 sc (Configuration Time) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2025 7110 m
+gs 1 -1 sc (Connection Duration) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2835 6885 m
+gs 1 -1 sc (Request Duration) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+6345 6795 m
+gs 1 -1 sc (t) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7110 5985 m
+gs 1 -1 sc (->client) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 5085 m
+gs 1 -1 sc (->connection) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 4770 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8010 5445 m
+gs 1 -1 sc (SSL_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10530 4050 m
+gs 1 -1 sc (->pSSLCtx) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7875 4275 m
+gs 1 -1 sc (SSL_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10305 5535 m
+gs 1 -1 sc (SSL_get_current_cipher\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10440 5940 m
+gs 1 -1 sc (SSL_get_session\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9540 7335 m
+gs 1 -1 sc (SSL_get_{r,w}bio\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10125 4680 m
+gs 1 -1 sc (SSL_get_SSL_CTX\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10350 5175 m
+gs 1 -1 sc (SSL_get_SSL_METHOD\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+11745 4770 m
+gs 1 -1 sc (->method) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9945 6480 m
+gs 1 -1 sc (X509_STORE_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10980 6705 m
+gs 1 -1 sc (SSL_CTX_get_cert_store\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 5130 m
+gs 1 -1 sc (SSL_get_app_data2\(\)) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3645 1620 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10935 3645 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10935 3825 m
+gs 1 -1 sc ([SSL]) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5760 m
+gs 1 -1 sc (SSL_CIPHER) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10980 6165 m
+gs 1 -1 sc (SSL_SESSION) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10710 7605 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10575 7110 m
+gs 1 -1 sc (X509_STORE_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6795 2430 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8865 2430 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 3555 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 4455 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 5355 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 5355 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10665 2430 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+7290 6255 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5355 m
+gs 1 -1 sc (SSL_METHOD) col0 sh gr
+% Polyline
+15.000 slw
+n 750 225 m 450 225 450 8250 300 arcto 4 {pop} repeat
+  450 8550 12300 8550 300 arcto 4 {pop} repeat
+  12600 8550 12600 525 300 arcto 4 {pop} repeat
+  12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col0 s gr 
+/Helvetica-Bold-iso ff 180.00 scf sf
+11475 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8010 4950 m
+gs 1 -1 sc (request_rec) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+10575 675 m
+gs 1 -1 sc (Ralf S. Engelschall) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+4275 675 m
+gs 1 -1 sc (Apache+mod_ssl+OpenSSL) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 855 m
+gs 1 -1 sc (rse@engelschall.com) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 1035 m
+gs 1 -1 sc (www.engelschall.com) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 675 m
+gs 1 -1 sc (Version 1.3) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 855 m
+gs 1 -1 sc (12-Apr-1999) col0 sh gr
+/Helvetica-Bold-iso ff 360.00 scf sf
+3915 1080 m
+gs 1 -1 sc (Data Structure Overview) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 7110 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10710 7785 m
+gs 1 -1 sc ([Crypto]) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8730 3465 m
+gs 1 -1 sc (mod_ssl) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8145 6750 m
+gs 1 -1 sc (Apache) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+9000 8100 m
+gs 1 -1 sc (Chaining) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+2745 8100 m
+gs 1 -1 sc (Lifetime) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+810 6255 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+990 5805 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4050 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4455 5355 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3870 4905 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+1845 4005 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2070 3555 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2295 3105 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2565 2655 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3915 2070 m
+gs 1 -1 sc (request_rec) col0 sh gr
+$F2psEnd
+rs
+showpage
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/config.m4 b/modules/http2/sandbox/httpd/mod_ssl-alpn/config.m4
new file mode 100644 (file)
index 0000000..45eeb43
--- /dev/null
@@ -0,0 +1,57 @@
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  You may obtain a copy of the License at
+dnl
+dnl      http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+
+dnl #  start of module specific part
+APACHE_MODPATH_INIT(ssl)
+
+dnl #  list of module object files
+ssl_objs="dnl
+mod_ssl.lo dnl
+ssl_engine_config.lo dnl
+ssl_engine_init.lo dnl
+ssl_engine_io.lo dnl
+ssl_engine_kernel.lo dnl
+ssl_engine_log.lo dnl
+ssl_engine_mutex.lo dnl
+ssl_engine_pphrase.lo dnl
+ssl_engine_rand.lo dnl
+ssl_engine_vars.lo dnl
+ssl_scache.lo dnl
+ssl_util_stapling.lo dnl
+ssl_util.lo dnl
+ssl_util_ssl.lo dnl
+ssl_engine_ocsp.lo dnl
+ssl_util_ocsp.lo dnl
+"
+dnl #  hook module into the Autoconf mechanism (--enable-ssl option)
+APACHE_MODULE(ssl, [SSL/TLS support (mod_ssl)], $ssl_objs, , most, [
+    APACHE_CHECK_OPENSSL
+    if test "$ac_cv_openssl" = "yes" ; then
+        if test "x$enable_ssl" = "xshared"; then
+           # The only symbol which needs to be exported is the module
+           # structure, so ask libtool to hide everything else:
+           APR_ADDTO(MOD_SSL_LDADD, [-export-symbols-regex ssl_module])
+        fi
+    else
+        enable_ssl=no
+    fi
+])
+
+# Ensure that other modules can pick up mod_ssl.h
+APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
+
+dnl #  end of module specific part
+APACHE_MODPATH_FINISH
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.c
new file mode 100644 (file)
index 0000000..2c465f6
--- /dev/null
@@ -0,0 +1,641 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  mod_ssl.c
+ *  Apache API interface structures
+ */
+
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "util_md5.h"
+#include "util_mutex.h"
+#include "ap_provider.h"
+
+#include <assert.h>
+
+/*
+ *  the table of configuration directives we provide
+ */
+
+#define SSL_CMD_ALL(name, args, desc) \
+        AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \
+                       NULL, RSRC_CONF|OR_AUTHCFG, desc),
+
+#define SSL_CMD_SRV(name, args, desc) \
+        AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \
+                       NULL, RSRC_CONF, desc),
+
+#define SSL_CMD_DIR(name, type, args, desc) \
+        AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \
+                       NULL, OR_##type, desc),
+
+#define AP_END_CMD { NULL }
+
+static const command_rec ssl_config_cmds[] = {
+    /*
+     * Global (main-server) context configuration directives
+     */
+    SSL_CMD_SRV(PassPhraseDialog, TAKE1,
+                "SSL dialog mechanism for the pass phrase query "
+                "('builtin', '|/path/to/pipe_program', "
+                "or 'exec:/path/to/cgi_program')")
+    SSL_CMD_SRV(SessionCache, TAKE1,
+                "SSL Session Cache storage "
+                "('none', 'nonenotnull', 'dbm:/path/to/file')")
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+    SSL_CMD_SRV(CryptoDevice, TAKE1,
+                "SSL external Crypto Device usage "
+                "('builtin', '...')")
+#endif
+    SSL_CMD_SRV(RandomSeed, TAKE23,
+                "SSL Pseudo Random Number Generator (PRNG) seeding source "
+                "('startup|connect builtin|file:/path|exec:/path [bytes]')")
+
+    /*
+     * Per-server context configuration directives
+     */
+    SSL_CMD_SRV(Engine, TAKE1,
+                "SSL switch for the protocol engine "
+                "('on', 'off')")
+    SSL_CMD_SRV(FIPS, FLAG,
+                "Enable FIPS-140 mode "
+                "(`on', `off')")
+    SSL_CMD_ALL(CipherSuite, TAKE1,
+                "Colon-delimited list of permitted SSL Ciphers "
+                "('XXX:...:XXX' - see manual)")
+    SSL_CMD_SRV(CertificateFile, TAKE1,
+                "SSL Server Certificate file "
+                "('/path/to/file' - PEM or DER encoded)")
+    SSL_CMD_SRV(CertificateKeyFile, TAKE1,
+                "SSL Server Private Key file "
+                "('/path/to/file' - PEM or DER encoded)")
+    SSL_CMD_SRV(CertificateChainFile, TAKE1,
+                "SSL Server CA Certificate Chain file "
+                "('/path/to/file' - PEM encoded)")
+#ifdef HAVE_TLS_SESSION_TICKETS
+    SSL_CMD_SRV(SessionTicketKeyFile, TAKE1,
+                "TLS session ticket encryption/decryption key file (RFC 5077) "
+                "('/path/to/file' - file with 48 bytes of random data)")
+#endif
+    SSL_CMD_ALL(CACertificatePath, TAKE1,
+                "SSL CA Certificate path "
+                "('/path/to/dir' - contains PEM encoded files)")
+    SSL_CMD_ALL(CACertificateFile, TAKE1,
+                "SSL CA Certificate file "
+                "('/path/to/file' - PEM encoded)")
+    SSL_CMD_SRV(CADNRequestPath, TAKE1,
+                "SSL CA Distinguished Name path "
+                "('/path/to/dir' - symlink hashes to PEM of acceptable CA names to request)")
+    SSL_CMD_SRV(CADNRequestFile, TAKE1,
+                "SSL CA Distinguished Name file "
+                "('/path/to/file' - PEM encoded to derive acceptable CA names to request)")
+    SSL_CMD_SRV(CARevocationPath, TAKE1,
+                "SSL CA Certificate Revocation List (CRL) path "
+                "('/path/to/dir' - contains PEM encoded files)")
+    SSL_CMD_SRV(CARevocationFile, TAKE1,
+                "SSL CA Certificate Revocation List (CRL) file "
+                "('/path/to/file' - PEM encoded)")
+    SSL_CMD_SRV(CARevocationCheck, TAKE1,
+                "SSL CA Certificate Revocation List (CRL) checking mode")
+    SSL_CMD_ALL(VerifyClient, TAKE1,
+                "SSL Client verify type "
+                "('none', 'optional', 'require', 'optional_no_ca')")
+    SSL_CMD_ALL(VerifyDepth, TAKE1,
+                "SSL Client verify depth "
+                "('N' - number of intermediate certificates)")
+    SSL_CMD_SRV(SessionCacheTimeout, TAKE1,
+                "SSL Session Cache object lifetime "
+                "('N' - number of seconds)")
+#ifdef HAVE_TLSV1_X
+#define SSL_PROTOCOLS "SSLv3|TLSv1|TLSv1.1|TLSv1.2"
+#else
+#define SSL_PROTOCOLS "SSLv3|TLSv1"
+#endif
+    SSL_CMD_SRV(Protocol, RAW_ARGS,
+                "Enable or disable various SSL protocols "
+                "('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
+    SSL_CMD_SRV(HonorCipherOrder, FLAG,
+                "Use the server's cipher ordering preference")
+    SSL_CMD_SRV(Compression, FLAG,
+                "Enable SSL level compression "
+                "(`on', `off')")
+    SSL_CMD_SRV(InsecureRenegotiation, FLAG,
+                "Enable support for insecure renegotiation")
+    SSL_CMD_ALL(UserName, TAKE1,
+                "Set user name to SSL variable value")
+    SSL_CMD_SRV(StrictSNIVHostCheck, FLAG,
+                "Strict SNI virtual host checking")
+
+#ifdef HAVE_SRP
+    SSL_CMD_SRV(SRPVerifierFile, TAKE1,
+                "SRP verifier file "
+                "('/path/to/file' - created by srptool)")
+    SSL_CMD_SRV(SRPUnknownUserSeed, TAKE1,
+                "SRP seed for unknown users (to avoid leaking a user's existence) "
+                "('some secret text')")
+#endif
+
+    /*
+     * Proxy configuration for remote SSL connections
+     */
+    SSL_CMD_SRV(ProxyEngine, FLAG,
+                "SSL switch for the proxy protocol engine "
+                "('on', 'off')")
+    SSL_CMD_SRV(ProxyProtocol, RAW_ARGS,
+               "SSL Proxy: enable or disable SSL protocol flavors "
+                "('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
+    SSL_CMD_SRV(ProxyCipherSuite, TAKE1,
+               "SSL Proxy: colon-delimited list of permitted SSL ciphers "
+               "('XXX:...:XXX' - see manual)")
+    SSL_CMD_SRV(ProxyVerify, TAKE1,
+               "SSL Proxy: whether to verify the remote certificate "
+               "('on' or 'off')")
+    SSL_CMD_SRV(ProxyVerifyDepth, TAKE1,
+               "SSL Proxy: maximum certificate verification depth "
+               "('N' - number of intermediate certificates)")
+    SSL_CMD_SRV(ProxyCACertificateFile, TAKE1,
+               "SSL Proxy: file containing server certificates "
+               "('/path/to/file' - PEM encoded certificates)")
+    SSL_CMD_SRV(ProxyCACertificatePath, TAKE1,
+               "SSL Proxy: directory containing server certificates "
+               "('/path/to/dir' - contains PEM encoded certificates)")
+    SSL_CMD_SRV(ProxyCARevocationPath, TAKE1,
+                "SSL Proxy: CA Certificate Revocation List (CRL) path "
+                "('/path/to/dir' - contains PEM encoded files)")
+    SSL_CMD_SRV(ProxyCARevocationFile, TAKE1,
+                "SSL Proxy: CA Certificate Revocation List (CRL) file "
+                "('/path/to/file' - PEM encoded)")
+    SSL_CMD_SRV(ProxyCARevocationCheck, TAKE1,
+                "SSL Proxy: CA Certificate Revocation List (CRL) checking mode")
+    SSL_CMD_SRV(ProxyMachineCertificateFile, TAKE1,
+               "SSL Proxy: file containing client certificates "
+               "('/path/to/file' - PEM encoded certificates)")
+    SSL_CMD_SRV(ProxyMachineCertificatePath, TAKE1,
+               "SSL Proxy: directory containing client certificates "
+               "('/path/to/dir' - contains PEM encoded certificates)")
+    SSL_CMD_SRV(ProxyMachineCertificateChainFile, TAKE1,
+               "SSL Proxy: file containing issuing certificates "
+               "of the client certificate "
+               "(`/path/to/file' - PEM encoded certificates)")
+    SSL_CMD_SRV(ProxyCheckPeerExpire, FLAG,
+                "SSL Proxy: check the peer certificate's expiration date")
+    SSL_CMD_SRV(ProxyCheckPeerCN, FLAG,
+                "SSL Proxy: check the peer certificate's CN")
+    SSL_CMD_SRV(ProxyCheckPeerName, FLAG,
+                "SSL Proxy: check the peer certificate's name "
+                "(must be present in subjectAltName extension or CN")
+
+    /*
+     * Per-directory context configuration directives
+     */
+    SSL_CMD_DIR(Options, OPTIONS, RAW_ARGS,
+               "Set one or more options to configure the SSL engine"
+               "('[+-]option[=value] ...' - see manual)")
+    SSL_CMD_DIR(RequireSSL, AUTHCFG, NO_ARGS,
+               "Require the SSL protocol for the per-directory context "
+               "(no arguments)")
+    SSL_CMD_DIR(Require, AUTHCFG, RAW_ARGS,
+               "Require a boolean expression to evaluate to true for granting access"
+               "(arbitrary complex boolean expression - see manual)")
+    SSL_CMD_DIR(RenegBufferSize, AUTHCFG, TAKE1,
+                "Configure the amount of memory that will be used for buffering the "
+                "request body if a per-location SSL renegotiation is required due to "
+                "changed access control requirements")
+
+    SSL_CMD_SRV(OCSPEnable, FLAG,
+               "Enable use of OCSP to verify certificate revocation ('on', 'off')")
+    SSL_CMD_SRV(OCSPDefaultResponder, TAKE1,
+               "URL of the default OCSP Responder")
+    SSL_CMD_SRV(OCSPOverrideResponder, FLAG,
+               "Force use of the default responder URL ('on', 'off')")
+    SSL_CMD_SRV(OCSPResponseTimeSkew, TAKE1,
+                "Maximum time difference in OCSP responses")
+    SSL_CMD_SRV(OCSPResponseMaxAge, TAKE1,
+                "Maximum age of OCSP responses")
+    SSL_CMD_SRV(OCSPResponderTimeout, TAKE1,
+                "OCSP responder query timeout")
+    SSL_CMD_SRV(OCSPUseRequestNonce, FLAG,
+                "Whether OCSP queries use a nonce or not ('on', 'off')")
+
+#ifdef HAVE_OCSP_STAPLING
+    /*
+     * OCSP Stapling options
+     */
+    SSL_CMD_SRV(StaplingCache, TAKE1,
+                "SSL Stapling Response Cache storage "
+                "(`dbm:/path/to/file')")
+    SSL_CMD_SRV(UseStapling, FLAG,
+                "SSL switch for the OCSP Stapling protocol " "(`on', `off')")
+    SSL_CMD_SRV(StaplingResponseTimeSkew, TAKE1,
+                "SSL stapling option for maximum time difference in OCSP responses")
+    SSL_CMD_SRV(StaplingResponderTimeout, TAKE1,
+                "SSL stapling option for OCSP responder timeout")
+    SSL_CMD_SRV(StaplingResponseMaxAge, TAKE1,
+                "SSL stapling option for maximum age of OCSP responses")
+    SSL_CMD_SRV(StaplingStandardCacheTimeout, TAKE1,
+                "SSL stapling option for normal OCSP Response Cache Lifetime")
+    SSL_CMD_SRV(StaplingReturnResponderErrors, FLAG,
+                "SSL stapling switch to return Status Errors Back to Client"
+                "(`on', `off')")
+    SSL_CMD_SRV(StaplingFakeTryLater, FLAG,
+                "SSL stapling switch to send tryLater response to client on error "
+                "(`on', `off')")
+    SSL_CMD_SRV(StaplingErrorCacheTimeout, TAKE1,
+                "SSL stapling option for OCSP Response Error Cache Lifetime")
+    SSL_CMD_SRV(StaplingForceURL, TAKE1,
+                "SSL stapling option to Force the OCSP Stapling URL")
+#endif
+
+#ifdef HAVE_SSL_CONF_CMD
+    SSL_CMD_SRV(OpenSSLConfCmd, TAKE2,
+               "OpenSSL configuration command")
+#endif
+
+#if defined(HAVE_ALPN_NPN) || defined(HAVE_TLS_NPN)
+    SSL_CMD_SRV(AlpnPreference, ITERATE,
+                "Preference in Application-Layer Protocol Negotiation (ALPN), "
+                "protocols are chosed in the specified order")
+#endif
+    
+    /* Deprecated directives. */
+    AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
+      "SSLLog directive is no longer supported - use ErrorLog."),
+    AP_INIT_RAW_ARGS("SSLLogLevel", ap_set_deprecated, NULL, OR_ALL,
+      "SSLLogLevel directive is no longer supported - use LogLevel."),
+
+    AP_END_CMD
+};
+
+/*
+ *  the various processing hooks
+ */
+static apr_status_t ssl_cleanup_pre_config(void *data)
+{
+    /*
+     * Try to kill the internals of the SSL library.
+     */
+    /* Corresponds to OPENSSL_load_builtin_modules():
+     * XXX: borrowed from apps.h, but why not CONF_modules_free()
+     * which also invokes CONF_modules_finish()?
+     */
+    CONF_modules_unload(1);
+    /* Corresponds to SSL_library_init: */
+    EVP_cleanup();
+#if HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+    ENGINE_cleanup();
+#endif
+    ERR_remove_state(0);
+
+    /* Don't call ERR_free_strings here; ERR_load_*_strings only
+     * actually load the error strings once per process due to static
+     * variable abuse in OpenSSL. */
+
+    /* Also don't call CRYPTO_cleanup_all_ex_data here; any registered
+     * ex_data indices may have been cached in static variables in
+     * OpenSSL; removing them may cause havoc.  Notably, with OpenSSL
+     * versions >= 0.9.8f, COMP_CTX cleanups would not be run, which
+     * could result in a per-connection memory leak (!). */
+
+    /*
+     * TODO: determine somewhere we can safely shove out diagnostics
+     *       (when enabled) at this late stage in the game:
+     * CRYPTO_mem_leaks_fp(stderr);
+     */
+    return APR_SUCCESS;
+}
+
+static int ssl_hook_pre_config(apr_pool_t *pconf,
+                               apr_pool_t *plog,
+                               apr_pool_t *ptemp)
+{
+    /* We must register the library in full, to ensure our configuration
+     * code can successfully test the SSL environment.
+     */
+    CRYPTO_malloc_init();
+    ERR_load_crypto_strings();
+    SSL_load_error_strings();
+    SSL_library_init();
+#if HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+    ENGINE_load_builtin_engines();
+#endif
+    OpenSSL_add_all_algorithms();
+    OPENSSL_load_builtin_modules();
+
+    /*
+     * Let us cleanup the ssl library when the module is unloaded
+     */
+    apr_pool_cleanup_register(pconf, NULL, ssl_cleanup_pre_config,
+                                           apr_pool_cleanup_null);
+
+    /* Register us to handle mod_log_config %c/%x variables */
+    ssl_var_log_config_register(pconf);
+
+    /* Register to handle mod_status status page generation */
+    ssl_scache_status_register(pconf);
+
+    /* Register mutex type names so they can be configured with Mutex */
+    ap_mutex_register(pconf, SSL_CACHE_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
+#ifdef HAVE_OCSP_STAPLING
+    ap_mutex_register(pconf, SSL_STAPLING_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
+#endif
+
+    return OK;
+}
+
+static SSLConnRec *ssl_init_connection_ctx(conn_rec *c)
+{
+    SSLConnRec *sslconn = myConnConfig(c);
+
+    if (sslconn) {
+        return sslconn;
+    }
+
+    sslconn = apr_pcalloc(c->pool, sizeof(*sslconn));
+
+    sslconn->server = c->base_server;
+    sslconn->verify_depth = UNSET;
+
+    myConnConfigSet(c, sslconn);
+
+    return sslconn;
+}
+
+int ssl_proxy_enable(conn_rec *c)
+{
+    SSLSrvConfigRec *sc;
+
+    SSLConnRec *sslconn = ssl_init_connection_ctx(c);
+    sc = mySrvConfig(sslconn->server);
+
+    if (!sc->proxy_enabled) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01961)
+                      "SSL Proxy requested for %s but not enabled "
+                      "[Hint: SSLProxyEngine]", sc->vhost_id);
+
+        return 0;
+    }
+
+    sslconn->is_proxy = 1;
+    sslconn->disabled = 0;
+
+    return 1;
+}
+
+int ssl_engine_disable(conn_rec *c)
+{
+    SSLSrvConfigRec *sc;
+
+    SSLConnRec *sslconn = myConnConfig(c);
+
+    if (sslconn) {
+        sc = mySrvConfig(sslconn->server);
+    }
+    else {
+        sc = mySrvConfig(c->base_server);
+    }
+    if (sc->enabled == SSL_ENABLED_FALSE) {
+        return 0;
+    }
+
+    sslconn = ssl_init_connection_ctx(c);
+
+    sslconn->disabled = 1;
+
+    return 1;
+}
+
+static int modssl_register_alpn(conn_rec *c,
+                               ssl_alpn_propose_protos advertisefn,
+                               ssl_alpn_proto_negotiated negotiatedfn)
+{
+#if defined(HAVE_ALPN_NPN) || defined(HAVE_TLS_NPN)
+       SSLConnRec *sslconn = myConnConfig(c);
+       
+       if (!sslconn) {
+               return DECLINED;
+       }
+       
+       if (!sslconn->alpn_proposefns) {
+               sslconn->alpn_proposefns =
+               apr_array_make(c->pool, 5, sizeof(ssl_alpn_propose_protos));
+               sslconn->alpn_negofns =
+               apr_array_make(c->pool, 5, sizeof(ssl_alpn_proto_negotiated));
+       }
+       
+       if (advertisefn)
+               APR_ARRAY_PUSH(sslconn->alpn_proposefns, ssl_alpn_propose_protos) =
+                       advertisefn;
+       if (negotiatedfn)
+               APR_ARRAY_PUSH(sslconn->alpn_negofns, ssl_alpn_proto_negotiated) =
+                       negotiatedfn;
+       
+       return OK;
+#else
+    return DECLINED;
+#endif
+}
+
+int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
+{
+    SSLSrvConfigRec *sc;
+    SSL *ssl;
+    SSLConnRec *sslconn = myConnConfig(c);
+    char *vhost_md5;
+    modssl_ctx_t *mctx;
+    server_rec *server;
+
+    if (!sslconn) {
+        sslconn = ssl_init_connection_ctx(c);
+    }
+    server = sslconn->server;
+    sc = mySrvConfig(server);
+
+    /*
+     * Seed the Pseudo Random Number Generator (PRNG)
+     */
+    ssl_rand_seed(server, c->pool, SSL_RSCTX_CONNECT, "");
+
+    mctx = sslconn->is_proxy ? sc->proxy : sc->server;
+
+    /*
+     * Create a new SSL connection with the configured server SSL context and
+     * attach this to the socket. Additionally we register this attachment
+     * so we can detach later.
+     */
+    if (!(ssl = SSL_new(mctx->ssl_ctx))) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01962)
+                      "Unable to create a new SSL connection from the SSL "
+                      "context");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, server);
+
+        c->aborted = 1;
+
+        return DECLINED; /* XXX */
+    }
+
+    vhost_md5 = ap_md5_binary(c->pool, (unsigned char *)sc->vhost_id,
+                              sc->vhost_id_len);
+
+    if (!SSL_set_session_id_context(ssl, (unsigned char *)vhost_md5,
+                                    APR_MD5_DIGESTSIZE*2))
+    {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01963)
+                      "Unable to set session id context to '%s'", vhost_md5);
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, server);
+
+        c->aborted = 1;
+
+        return DECLINED; /* XXX */
+    }
+
+    SSL_set_app_data(ssl, c);
+    SSL_set_app_data2(ssl, NULL); /* will be request_rec */
+
+    sslconn->ssl = ssl;
+
+    SSL_set_verify_result(ssl, X509_V_OK);
+
+    ssl_io_filter_init(c, r, ssl);
+
+    return APR_SUCCESS;
+}
+
+static const char *ssl_hook_http_scheme(const request_rec *r)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+
+    if (sc->enabled == SSL_ENABLED_FALSE || sc->enabled == SSL_ENABLED_OPTIONAL) {
+        return NULL;
+    }
+
+    return "https";
+}
+
+static apr_port_t ssl_hook_default_port(const request_rec *r)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+
+    if (sc->enabled == SSL_ENABLED_FALSE || sc->enabled == SSL_ENABLED_OPTIONAL) {
+        return 0;
+    }
+
+    return 443;
+}
+
+static int ssl_hook_pre_connection(conn_rec *c, void *csd)
+{
+    SSLSrvConfigRec *sc;
+    SSLConnRec *sslconn = myConnConfig(c);
+
+    if (sslconn) {
+        sc = mySrvConfig(sslconn->server);
+    }
+    else {
+        sc = mySrvConfig(c->base_server);
+    }
+    /*
+     * Immediately stop processing if SSL is disabled for this connection
+     */
+    if (!(sc && (sc->enabled == SSL_ENABLED_TRUE ||
+                 (sslconn && sslconn->is_proxy))))
+    {
+        return DECLINED;
+    }
+
+    /*
+     * Create SSL context
+     */
+    if (!sslconn) {
+        sslconn = ssl_init_connection_ctx(c);
+    }
+
+    if (sslconn->disabled) {
+        return DECLINED;
+    }
+
+    /*
+     * Remember the connection information for
+     * later access inside callback functions
+     */
+
+    ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(01964)
+                  "Connection to child %ld established "
+                  "(server %s)", c->id, sc->vhost_id);
+
+    return ssl_init_ssl_connection(c, NULL);
+}
+
+/*
+ *  the module registration phase
+ */
+
+static void ssl_register_hooks(apr_pool_t *p)
+{
+    /* ssl_hook_ReadReq needs to use the BrowserMatch settings so must
+     * run after mod_setenvif's post_read_request hook. */
+    static const char *pre_prr[] = { "mod_setenvif.c", NULL };
+
+    ssl_io_filter_register(p);
+
+    ap_hook_pre_connection(ssl_hook_pre_connection,NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_test_config   (ssl_hook_ConfigTest,    NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_post_config   (ssl_init_Module,        NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_http_scheme   (ssl_hook_http_scheme,   NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_default_port  (ssl_hook_default_port,  NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_pre_config    (ssl_hook_pre_config,    NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_child_init    (ssl_init_Child,         NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_check_authn   (ssl_hook_UserCheck,     NULL,NULL, APR_HOOK_FIRST,
+                           AP_AUTH_INTERNAL_PER_CONF);
+    ap_hook_fixups        (ssl_hook_Fixup,         NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_check_access  (ssl_hook_Access,        NULL,NULL, APR_HOOK_MIDDLE,
+                           AP_AUTH_INTERNAL_PER_CONF);
+    ap_hook_check_authz   (ssl_hook_Auth,          NULL,NULL, APR_HOOK_MIDDLE,
+                           AP_AUTH_INTERNAL_PER_CONF);
+    ap_hook_post_read_request(ssl_hook_ReadReq, pre_prr,NULL, APR_HOOK_MIDDLE);
+
+    ssl_var_register(p);
+
+    APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
+    APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
+    APR_REGISTER_OPTIONAL_FN(modssl_register_alpn);
+
+    ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
+                              AUTHZ_PROVIDER_VERSION,
+                              &ssl_authz_provider_require_ssl,
+                              AP_AUTH_INTERNAL_PER_CONF);
+
+    ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl-verify-client",
+                              AUTHZ_PROVIDER_VERSION,
+                              &ssl_authz_provider_verify_client,
+                              AP_AUTH_INTERNAL_PER_CONF);
+
+}
+
+module AP_MODULE_DECLARE_DATA ssl_module = {
+    STANDARD20_MODULE_STUFF,
+    ssl_config_perdir_create,   /* create per-dir    config structures */
+    ssl_config_perdir_merge,    /* merge  per-dir    config structures */
+    ssl_config_server_create,   /* create per-server config structures */
+    ssl_config_server_merge,    /* merge  per-server config structures */
+    ssl_config_cmds,            /* table of configuration directives   */
+    ssl_register_hooks          /* register hooks */
+};
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.dsp b/modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.dsp
new file mode 100644 (file)
index 0000000..58b5545
--- /dev/null
@@ -0,0 +1,195 @@
+# Microsoft Developer Studio Project File - Name="mod_ssl" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_ssl - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_ssl.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_ssl.mak" CFG="mod_ssl - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_ssl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_ssl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_ssl - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../generators" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/openssl/inc32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D "HAVE_SSL_SET_STATE" /D "HAVE_OPENSSL_ENGINE_H" /D "HAVE_ENGINE_INIT" /D "HAVE_ENGINE_LOAD_BUILTIN_ENGINES" /Fd"Release\mod_ssl_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_ssl.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so
+# ADD LINK32 kernel32.lib user32.lib wsock32.lib ws2_32.lib advapi32.lib gdi32.lib libeay32.lib ssleay32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_ssl.so" /libpath:"../../srclib/openssl/out32dll" /libpath:"../../srclib/openssl/out32" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_ssl.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "mod_ssl - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../generators" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/openssl/inc32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D "HAVE_SSL_SET_STATE" /D "HAVE_OPENSSL_ENGINE_H" /D "HAVE_ENGINE_INIT" /D "HAVE_ENGINE_LOAD_BUILTIN_ENGINES" /Fd"Debug\mod_ssl_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_ssl.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_ssl.so" /d LONG_NAME="proxy_ssl_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_ssl.so" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so
+# ADD LINK32 kernel32.lib user32.lib wsock32.lib ws2_32.lib advapi32.lib gdi32.lib libeay32.lib ssleay32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_ssl.so" /libpath:"../../srclib/openssl/out32dll.dbg" /libpath:"../../srclib/openssl/out32.dbg" /libpath:"../../srclib/openssl/out32dll" /libpath:"../../srclib/openssl/out32" /base:@..\..\os\win32\BaseAddr.ref,mod_ssl.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_ssl.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_ssl - Win32 Release"
+# Name "mod_ssl - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\mod_ssl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_init.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_io.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_kernel.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_log.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_pphrase.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_rand.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_vars.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_engine_ocsp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ocsp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_scache.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_stapling.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ssl.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\mod_ssl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_private.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_ssl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ssl_util_table.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.h b/modules/http2/sandbox/httpd/mod_ssl-alpn/mod_ssl.h
new file mode 100644 (file)
index 0000000..2b061cb
--- /dev/null
@@ -0,0 +1,108 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file mod_ssl.h
+ * @brief SSL extension module for Apache
+ *
+ * @defgroup MOD_SSL mod_ssl
+ * @ingroup  APACHE_MODS
+ * @{
+ */
+
+#ifndef __MOD_SSL_H__
+#define __MOD_SSL_H__
+
+#include "httpd.h"
+#include "apr_optional.h"
+
+/** The ssl_var_lookup() optional function retrieves SSL environment
+ * variables. */
+APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
+                        (apr_pool_t *, server_rec *,
+                         conn_rec *, request_rec *,
+                         char *));
+
+/** The ssl_ext_list() optional function attempts to build an array
+ * of all the values contained in the named X.509 extension. The
+ * returned array will be created in the supplied pool.
+ * The client certificate is used if peer is non-zero; the server
+ * certificate is used otherwise.
+ * Extension specifies the extensions to use as a string. This can be
+ * one of the "known" long or short names, or a numeric OID,
+ * e.g. "1.2.3.4", 'nsComment' and 'DN' are all valid.
+ * A pointer to an apr_array_header_t structure is returned if at
+ * least one matching extension is found, NULL otherwise.
+ */
+APR_DECLARE_OPTIONAL_FN(apr_array_header_t *, ssl_ext_list,
+                        (apr_pool_t *p, conn_rec *c, int peer,
+                         const char *extension));
+
+/** An optional function which returns non-zero if the given connection
+ * is using SSL/TLS. */
+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
+
+/** The ssl_proxy_enable() and ssl_engine_disable() optional functions
+ * are used by mod_proxy to enable use of SSL for outgoing
+ * connections. */
+
+APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
+
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
+
+/** The alpn_propose_proto callback allows other modules to propose
+ * the name of the protocol that will be chosen during the
+ * Application-Layer Protocol Negotiation (ALPN) portion of the SSL handshake.
+ * The callback is given the connection and a list of NULL-terminated
+ * protocol strings as supported by the client.  If this client_protos is 
+ * non-empty, it must pick its preferred protocol from that list. Otherwise
+ * it should add its supported protocols in order of precedence.
+ * The callback should not yet modify the connection or install any filters
+ * as its proposal(s) may be overridden by another callback or server 
+ * configuration. 
+ * It should return OK or, to prevent further processing of (other modules') 
+ * callbacks, return DONE.
+ */
+typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
+                                                                       apr_array_header_t *client_protos,
+                                                                       apr_array_header_t *proposed_protos);
+
+/** The alpn_proto_negotiated callback allows other modules to discover
+ * the name of the protocol that was chosen during the Application-Layer
+ * Protocol Negotiation (ALPN) portion of the SSL handshake.  
+ * The callback is given the connection, a
+ * non-NUL-terminated string containing the protocol name, and the
+ * length of the string; it should do something appropriate
+ * (i.e. insert or remove filters) and return OK. To prevent further
+ * processing of (other modules') callbacks, return DONE. */
+typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
+                                        const char *proto_name,
+                                        apr_size_t proto_name_len);
+
+/* An optional function which can be used to register a pair of callbacks 
+ * for ALPN handling.
+ * This optional function should be invoked from a pre_connection hook 
+ * which runs *after* mod_ssl.c's pre_connection hook.  The function returns 
+ * OK if the callbacks are registered, or DECLINED otherwise (for example if 
+ * mod_ssl does not support ALPN).
+ */
+APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
+                                               (conn_rec *conn,
+                                                ssl_alpn_propose_protos proposefn,
+                                                ssl_alpn_proto_negotiated negotiatedfn));
+
+#endif /* __MOD_SSL_H__ */
+/** @} */
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/modules.mk b/modules/http2/sandbox/httpd/mod_ssl-alpn/modules.mk
new file mode 100644 (file)
index 0000000..11edd08
--- /dev/null
@@ -0,0 +1,7 @@
+mod_ssl.la: mod_ssl.slo ssl_engine_config.slo ssl_engine_init.slo ssl_engine_io.slo ssl_engine_kernel.slo ssl_engine_log.slo ssl_engine_mutex.slo ssl_engine_pphrase.slo ssl_engine_rand.slo ssl_engine_vars.slo ssl_scache.slo ssl_util_stapling.slo ssl_util.slo ssl_util_ssl.slo ssl_engine_ocsp.slo ssl_util_ocsp.slo
+       $(SH_LINK) -rpath $(libexecdir) -module -avoid-version  mod_ssl.lo ssl_engine_config.lo ssl_engine_init.lo ssl_engine_io.lo ssl_engine_kernel.lo ssl_engine_log.lo ssl_engine_mutex.lo ssl_engine_pphrase.lo ssl_engine_rand.lo ssl_engine_vars.lo ssl_scache.lo ssl_util_stapling.lo ssl_util.lo ssl_util_ssl.lo ssl_engine_ocsp.lo ssl_util_ocsp.lo  $(MOD_SSL_LDADD)
+DISTCLEAN_TARGETS = modules.mk
+static = 
+shared =  mod_ssl.la
+MOD_CFLAGS = -I/Users/sei/projects/mod-h2/httpd/gen/build/include
+MOD_LDFLAGS = -L/Users/sei/projects/mod-h2/httpd/gen/build/lib -lssl -lcrypto -lpthread
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_config.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_config.c
new file mode 100644 (file)
index 0000000..2444d12
--- /dev/null
@@ -0,0 +1,1931 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_config.c
+ *  Apache Configuration Directives
+ */
+                                      /* ``Damned if you do,
+                                           damned if you don't.''
+                                               -- Unknown        */
+#include "ssl_private.h"
+#include "util_mutex.h"
+#include "ap_provider.h"
+
+/*  _________________________________________________________________
+**
+**  Support for Global Configuration
+**  _________________________________________________________________
+*/
+
+#define SSL_MOD_CONFIG_KEY "ssl_module"
+
+SSLModConfigRec *ssl_config_global_create(server_rec *s)
+{
+    apr_pool_t *pool = s->process->pool;
+    SSLModConfigRec *mc;
+    void *vmc;
+
+    apr_pool_userdata_get(&vmc, SSL_MOD_CONFIG_KEY, pool);
+    if (vmc) {
+        return vmc; /* reused for lifetime of the server */
+    }
+
+    /*
+     * allocate an own subpool which survives server restarts
+     */
+    mc = (SSLModConfigRec *)apr_palloc(pool, sizeof(*mc));
+    mc->pPool = pool;
+    mc->bFixed = FALSE;
+
+    /*
+     * initialize per-module configuration
+     */
+    mc->sesscache_mode         = SSL_SESS_CACHE_OFF;
+    mc->sesscache              = NULL;
+    mc->pMutex                 = NULL;
+    mc->aRandSeed              = apr_array_make(pool, 4,
+                                                sizeof(ssl_randseed_t));
+    mc->tVHostKeys             = apr_hash_make(pool);
+    mc->tPrivateKey            = apr_hash_make(pool);
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+    mc->szCryptoDevice         = NULL;
+#endif
+#ifdef HAVE_OCSP_STAPLING
+    mc->stapling_cache         = NULL;
+    mc->stapling_mutex         = NULL;
+#endif
+
+    apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY,
+                          apr_pool_cleanup_null,
+                          pool);
+
+    return mc;
+}
+
+void ssl_config_global_fix(SSLModConfigRec *mc)
+{
+    mc->bFixed = TRUE;
+}
+
+BOOL ssl_config_global_isfixed(SSLModConfigRec *mc)
+{
+    return mc->bFixed;
+}
+
+/*  _________________________________________________________________
+**
+**  Configuration handling
+**  _________________________________________________________________
+*/
+
+static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p)
+{
+    mctx->sc                  = NULL; /* set during module init */
+
+    mctx->ssl_ctx             = NULL; /* set during module init */
+
+    mctx->pks                 = NULL;
+    mctx->pkp                 = NULL;
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+    mctx->ticket_key          = NULL;
+#endif
+
+    mctx->protocol            = SSL_PROTOCOL_ALL;
+
+    mctx->pphrase_dialog_type = SSL_PPTYPE_UNSET;
+    mctx->pphrase_dialog_path = NULL;
+
+    mctx->cert_chain          = NULL;
+
+    mctx->crl_path            = NULL;
+    mctx->crl_file            = NULL;
+    mctx->crl_check_mode      = SSL_CRLCHECK_UNSET;
+
+    mctx->auth.ca_cert_path   = NULL;
+    mctx->auth.ca_cert_file   = NULL;
+    mctx->auth.cipher_suite   = NULL;
+    mctx->auth.verify_depth   = UNSET;
+    mctx->auth.verify_mode    = SSL_CVERIFY_UNSET;
+
+    mctx->ocsp_enabled        = FALSE;
+    mctx->ocsp_force_default  = FALSE;
+    mctx->ocsp_responder      = NULL;
+    mctx->ocsp_resptime_skew  = UNSET;
+    mctx->ocsp_resp_maxage    = UNSET;
+    mctx->ocsp_responder_timeout = UNSET;
+    mctx->ocsp_use_request_nonce = UNSET;
+
+#ifdef HAVE_OCSP_STAPLING
+    mctx->stapling_enabled           = UNSET;
+    mctx->stapling_resptime_skew     = UNSET;
+    mctx->stapling_resp_maxage       = UNSET;
+    mctx->stapling_cache_timeout     = UNSET;
+    mctx->stapling_return_errors     = UNSET;
+    mctx->stapling_fake_trylater     = UNSET;
+    mctx->stapling_errcache_timeout  = UNSET;
+    mctx->stapling_responder_timeout = UNSET;
+    mctx->stapling_force_url         = NULL;
+#endif
+
+#ifdef HAVE_SRP
+    mctx->srp_vfile =             NULL;
+    mctx->srp_unknown_user_seed = NULL;
+    mctx->srp_vbase =             NULL;
+#endif
+#ifdef HAVE_SSL_CONF_CMD
+    mctx->ssl_ctx_config = SSL_CONF_CTX_new();
+    SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_FILE);
+    SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_SERVER);
+    SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
+    mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
+#endif
+#if defined(HAVE_ALPN_NPN) || defined(HAVE_TLS_NPN)
+    mctx->ssl_alpn_pref = apr_array_make(p, 5, sizeof(const char *));
+#endif
+}
+
+static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc,
+                                  apr_pool_t *p)
+{
+    modssl_ctx_t *mctx;
+
+    mctx = sc->proxy = apr_palloc(p, sizeof(*sc->proxy));
+
+    modssl_ctx_init(mctx, p);
+
+    mctx->pkp = apr_palloc(p, sizeof(*mctx->pkp));
+
+    mctx->pkp->cert_file = NULL;
+    mctx->pkp->cert_path = NULL;
+    mctx->pkp->ca_cert_file = NULL;
+    mctx->pkp->certs     = NULL;
+    mctx->pkp->ca_certs  = NULL;
+}
+
+static void modssl_ctx_init_server(SSLSrvConfigRec *sc,
+                                   apr_pool_t *p)
+{
+    modssl_ctx_t *mctx;
+
+    mctx = sc->server = apr_palloc(p, sizeof(*sc->server));
+
+    modssl_ctx_init(mctx, p);
+
+    mctx->pks = apr_pcalloc(p, sizeof(*mctx->pks));
+
+    mctx->pks->cert_files = apr_array_make(p, 3, sizeof(char *));
+    mctx->pks->key_files  = apr_array_make(p, 3, sizeof(char *));
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+    mctx->ticket_key = apr_pcalloc(p, sizeof(*mctx->ticket_key));
+#endif
+}
+
+static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
+{
+    SSLSrvConfigRec *sc = apr_palloc(p, sizeof(*sc));
+
+    sc->mc                     = NULL;
+    sc->enabled                = SSL_ENABLED_UNSET;
+    sc->proxy_enabled          = UNSET;
+    sc->vhost_id               = NULL;  /* set during module init */
+    sc->vhost_id_len           = 0;     /* set during module init */
+    sc->session_cache_timeout  = UNSET;
+    sc->cipher_server_pref     = UNSET;
+    sc->insecure_reneg         = UNSET;
+    sc->proxy_ssl_check_peer_expire = SSL_ENABLED_UNSET;
+    sc->proxy_ssl_check_peer_cn     = SSL_ENABLED_UNSET;
+    sc->proxy_ssl_check_peer_name   = SSL_ENABLED_UNSET;
+#ifdef HAVE_TLSEXT
+    sc->strict_sni_vhost_check = SSL_ENABLED_UNSET;
+#endif
+#ifdef HAVE_FIPS
+    sc->fips                   = UNSET;
+#endif
+#ifndef OPENSSL_NO_COMP
+    sc->compression            = UNSET;
+#endif
+
+    modssl_ctx_init_proxy(sc, p);
+
+    modssl_ctx_init_server(sc, p);
+
+    return sc;
+}
+
+/*
+ *  Create per-server SSL configuration
+ */
+void *ssl_config_server_create(apr_pool_t *p, server_rec *s)
+{
+    SSLSrvConfigRec *sc = ssl_config_server_new(p);
+
+    sc->mc = ssl_config_global_create(s);
+
+    return sc;
+}
+
+#define cfgMerge(el,unset)  mrg->el = (add->el == (unset)) ? base->el : add->el
+#define cfgMergeArray(el)   mrg->el = apr_array_append(p, base->el, add->el)
+#define cfgMergeString(el)  cfgMerge(el, NULL)
+#define cfgMergeBool(el)    cfgMerge(el, UNSET)
+#define cfgMergeInt(el)     cfgMerge(el, UNSET)
+
+static void modssl_ctx_cfg_merge(apr_pool_t *p,
+                                 modssl_ctx_t *base,
+                                 modssl_ctx_t *add,
+                                 modssl_ctx_t *mrg)
+{
+    cfgMerge(protocol, SSL_PROTOCOL_ALL);
+
+    cfgMerge(pphrase_dialog_type, SSL_PPTYPE_UNSET);
+    cfgMergeString(pphrase_dialog_path);
+
+    cfgMergeString(cert_chain);
+
+    cfgMerge(crl_path, NULL);
+    cfgMerge(crl_file, NULL);
+    cfgMerge(crl_check_mode, SSL_CRLCHECK_UNSET);
+
+    cfgMergeString(auth.ca_cert_path);
+    cfgMergeString(auth.ca_cert_file);
+    cfgMergeString(auth.cipher_suite);
+    cfgMergeInt(auth.verify_depth);
+    cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET);
+
+    cfgMergeBool(ocsp_enabled);
+    cfgMergeBool(ocsp_force_default);
+    cfgMerge(ocsp_responder, NULL);
+    cfgMergeInt(ocsp_resptime_skew);
+    cfgMergeInt(ocsp_resp_maxage);
+    cfgMergeInt(ocsp_responder_timeout);
+    cfgMergeBool(ocsp_use_request_nonce);
+#ifdef HAVE_OCSP_STAPLING
+    cfgMergeBool(stapling_enabled);
+    cfgMergeInt(stapling_resptime_skew);
+    cfgMergeInt(stapling_resp_maxage);
+    cfgMergeInt(stapling_cache_timeout);
+    cfgMergeBool(stapling_return_errors);
+    cfgMergeBool(stapling_fake_trylater);
+    cfgMergeInt(stapling_errcache_timeout);
+    cfgMergeInt(stapling_responder_timeout);
+    cfgMerge(stapling_force_url, NULL);
+#endif
+
+#ifdef HAVE_SRP
+    cfgMergeString(srp_vfile);
+    cfgMergeString(srp_unknown_user_seed);
+#endif
+
+#ifdef HAVE_SSL_CONF_CMD
+    cfgMergeArray(ssl_ctx_param);
+#endif
+#if defined(HAVE_ALPN_NPN) || defined(HAVE_TLS_NPN)
+    cfgMergeArray(ssl_alpn_pref);
+#endif
+}
+
+static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
+                                       modssl_ctx_t *base,
+                                       modssl_ctx_t *add,
+                                       modssl_ctx_t *mrg)
+{
+    modssl_ctx_cfg_merge(p, base, add, mrg);
+
+    cfgMergeString(pkp->cert_file);
+    cfgMergeString(pkp->cert_path);
+    cfgMergeString(pkp->ca_cert_file);
+}
+
+static void modssl_ctx_cfg_merge_certkeys_array(apr_pool_t *p,
+                                                apr_array_header_t *base,
+                                                apr_array_header_t *add,
+                                                apr_array_header_t *mrg)
+{
+    int i;
+
+    /*
+     * pick up to CERTKEYS_IDX_MAX+1 entries from "add" (in which case they
+     * they "knock out" their corresponding entries in "base", emulating
+     * the behavior with cfgMergeString in releases up to 2.4.7)
+     */
+    for (i = 0; i < add->nelts && i <= CERTKEYS_IDX_MAX; i++) {
+        APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(add, i, const char *);
+    }
+
+    /* add remaining ones from "base" */
+    while (i < base->nelts) {
+        APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(base, i, const char *);
+        i++;
+    }
+
+    /* and finally, append the rest of "add" (if there are any) */
+    for (i = CERTKEYS_IDX_MAX+1; i < add->nelts; i++) {
+        APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(add, i, const char *);
+    }
+}
+
+static void modssl_ctx_cfg_merge_server(apr_pool_t *p,
+                                        modssl_ctx_t *base,
+                                        modssl_ctx_t *add,
+                                        modssl_ctx_t *mrg)
+{
+    modssl_ctx_cfg_merge(p, base, add, mrg);
+
+    /*
+     * For better backwards compatibility with releases up to 2.4.7,
+     * merging global and vhost-level SSLCertificateFile and
+     * SSLCertificateKeyFile directives needs special treatment.
+     * See also PR 56306 and 56353.
+     */
+    modssl_ctx_cfg_merge_certkeys_array(p, base->pks->cert_files,
+                                        add->pks->cert_files,
+                                        mrg->pks->cert_files);
+    modssl_ctx_cfg_merge_certkeys_array(p, base->pks->key_files,
+                                        add->pks->key_files,
+                                        mrg->pks->key_files);
+
+    cfgMergeString(pks->ca_name_path);
+    cfgMergeString(pks->ca_name_file);
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+    cfgMergeString(ticket_key->file_path);
+#endif
+}
+
+/*
+ *  Merge per-server SSL configurations
+ */
+void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
+{
+    SSLSrvConfigRec *base = (SSLSrvConfigRec *)basev;
+    SSLSrvConfigRec *add  = (SSLSrvConfigRec *)addv;
+    SSLSrvConfigRec *mrg  = ssl_config_server_new(p);
+
+    cfgMerge(mc, NULL);
+    cfgMerge(enabled, SSL_ENABLED_UNSET);
+    cfgMergeBool(proxy_enabled);
+    cfgMergeInt(session_cache_timeout);
+    cfgMergeBool(cipher_server_pref);
+    cfgMergeBool(insecure_reneg);
+    cfgMerge(proxy_ssl_check_peer_expire, SSL_ENABLED_UNSET);
+    cfgMerge(proxy_ssl_check_peer_cn, SSL_ENABLED_UNSET);
+    cfgMerge(proxy_ssl_check_peer_name, SSL_ENABLED_UNSET);
+#ifdef HAVE_TLSEXT
+    cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET);
+#endif
+#ifdef HAVE_FIPS
+    cfgMergeBool(fips);
+#endif
+#ifndef OPENSSL_NO_COMP
+    cfgMergeBool(compression);
+#endif
+
+    modssl_ctx_cfg_merge_proxy(p, base->proxy, add->proxy, mrg->proxy);
+
+    modssl_ctx_cfg_merge_server(p, base->server, add->server, mrg->server);
+
+    return mrg;
+}
+
+/*
+ *  Create per-directory SSL configuration
+ */
+void *ssl_config_perdir_create(apr_pool_t *p, char *dir)
+{
+    SSLDirConfigRec *dc = apr_palloc(p, sizeof(*dc));
+
+    dc->bSSLRequired  = FALSE;
+    dc->aRequirement  = apr_array_make(p, 4, sizeof(ssl_require_t));
+    dc->nOptions      = SSL_OPT_NONE|SSL_OPT_RELSET;
+    dc->nOptionsAdd   = SSL_OPT_NONE;
+    dc->nOptionsDel   = SSL_OPT_NONE;
+
+    dc->szCipherSuite          = NULL;
+    dc->nVerifyClient          = SSL_CVERIFY_UNSET;
+    dc->nVerifyDepth           = UNSET;
+
+    dc->szCACertificatePath    = NULL;
+    dc->szCACertificateFile    = NULL;
+    dc->szUserName             = NULL;
+
+    dc->nRenegBufferSize = UNSET;
+
+    return dc;
+}
+
+/*
+ *  Merge per-directory SSL configurations
+ */
+void *ssl_config_perdir_merge(apr_pool_t *p, void *basev, void *addv)
+{
+    SSLDirConfigRec *base = (SSLDirConfigRec *)basev;
+    SSLDirConfigRec *add  = (SSLDirConfigRec *)addv;
+    SSLDirConfigRec *mrg  = (SSLDirConfigRec *)apr_palloc(p, sizeof(*mrg));
+
+    cfgMerge(bSSLRequired, FALSE);
+    cfgMergeArray(aRequirement);
+
+    if (add->nOptions & SSL_OPT_RELSET) {
+        mrg->nOptionsAdd =
+            (base->nOptionsAdd & ~(add->nOptionsDel)) | add->nOptionsAdd;
+        mrg->nOptionsDel =
+            (base->nOptionsDel & ~(add->nOptionsAdd)) | add->nOptionsDel;
+        mrg->nOptions    =
+            (base->nOptions    & ~(mrg->nOptionsDel)) | mrg->nOptionsAdd;
+    }
+    else {
+        mrg->nOptions    = add->nOptions;
+        mrg->nOptionsAdd = add->nOptionsAdd;
+        mrg->nOptionsDel = add->nOptionsDel;
+    }
+
+    cfgMergeString(szCipherSuite);
+    cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET);
+    cfgMergeInt(nVerifyDepth);
+
+    cfgMergeString(szCACertificatePath);
+    cfgMergeString(szCACertificateFile);
+    cfgMergeString(szUserName);
+
+    cfgMergeInt(nRenegBufferSize);
+
+    return mrg;
+}
+
+/*
+ *  Configuration functions for particular directives
+ */
+
+const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *cmd,
+                                        void *dcfg,
+                                        const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+    int arglen = strlen(arg);
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    if (strcEQ(arg, "builtin")) {
+        sc->server->pphrase_dialog_type  = SSL_PPTYPE_BUILTIN;
+        sc->server->pphrase_dialog_path = NULL;
+    }
+    else if ((arglen > 5) && strEQn(arg, "exec:", 5)) {
+        sc->server->pphrase_dialog_type  = SSL_PPTYPE_FILTER;
+        sc->server->pphrase_dialog_path =
+            ap_server_root_relative(cmd->pool, arg+5);
+        if (!sc->server->pphrase_dialog_path) {
+            return apr_pstrcat(cmd->pool,
+                               "Invalid SSLPassPhraseDialog exec: path ",
+                               arg+5, NULL);
+        }
+        if (!ssl_util_path_check(SSL_PCM_EXISTS,
+                                 sc->server->pphrase_dialog_path,
+                                 cmd->pool))
+        {
+            return apr_pstrcat(cmd->pool,
+                               "SSLPassPhraseDialog: file '",
+                               sc->server->pphrase_dialog_path,
+                               "' does not exist", NULL);
+        }
+
+    }
+    else if ((arglen > 1) && (arg[0] == '|')) {
+        sc->server->pphrase_dialog_type  = SSL_PPTYPE_PIPE;
+        sc->server->pphrase_dialog_path = arg + 1;
+    }
+    else {
+        return "SSLPassPhraseDialog: Invalid argument";
+    }
+
+    return NULL;
+}
+
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+const char *ssl_cmd_SSLCryptoDevice(cmd_parms *cmd,
+                                    void *dcfg,
+                                    const char *arg)
+{
+    SSLModConfigRec *mc = myModConfig(cmd->server);
+    const char *err;
+    ENGINE *e;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    if (strcEQ(arg, "builtin")) {
+        mc->szCryptoDevice = NULL;
+    }
+    else if ((e = ENGINE_by_id(arg))) {
+        mc->szCryptoDevice = arg;
+        ENGINE_free(e);
+    }
+    else {
+        err = "SSLCryptoDevice: Invalid argument; must be one of: "
+              "'builtin' (none)";
+        e = ENGINE_get_first();
+        while (e) {
+            err = apr_pstrcat(cmd->pool, err, ", '", ENGINE_get_id(e),
+                                         "' (", ENGINE_get_name(e), ")", NULL);
+            /* Iterate; this call implicitly decrements the refcount
+             * on the 'old' e, per the docs in engine.h. */
+            e = ENGINE_get_next(e);
+        }
+        return err;
+    }
+
+    return NULL;
+}
+#endif
+
+const char *ssl_cmd_SSLRandomSeed(cmd_parms *cmd,
+                                  void *dcfg,
+                                  const char *arg1,
+                                  const char *arg2,
+                                  const char *arg3)
+{
+    SSLModConfigRec *mc = myModConfig(cmd->server);
+    const char *err;
+    ssl_randseed_t *seed;
+    int arg2len = strlen(arg2);
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    if (ssl_config_global_isfixed(mc)) {
+        return NULL;
+    }
+
+    seed = apr_array_push(mc->aRandSeed);
+
+    if (strcEQ(arg1, "startup")) {
+        seed->nCtx = SSL_RSCTX_STARTUP;
+    }
+    else if (strcEQ(arg1, "connect")) {
+        seed->nCtx = SSL_RSCTX_CONNECT;
+    }
+    else {
+        return apr_pstrcat(cmd->pool, "SSLRandomSeed: "
+                           "invalid context: `", arg1, "'",
+                           NULL);
+    }
+
+    if ((arg2len > 5) && strEQn(arg2, "file:", 5)) {
+        seed->nSrc   = SSL_RSSRC_FILE;
+        seed->cpPath = ap_server_root_relative(mc->pPool, arg2+5);
+    }
+    else if ((arg2len > 5) && strEQn(arg2, "exec:", 5)) {
+        seed->nSrc   = SSL_RSSRC_EXEC;
+        seed->cpPath = ap_server_root_relative(mc->pPool, arg2+5);
+    }
+    else if ((arg2len > 4) && strEQn(arg2, "egd:", 4)) {
+        seed->nSrc   = SSL_RSSRC_EGD;
+        seed->cpPath = ap_server_root_relative(mc->pPool, arg2+4);
+    }
+    else if (strcEQ(arg2, "builtin")) {
+        seed->nSrc   = SSL_RSSRC_BUILTIN;
+        seed->cpPath = NULL;
+    }
+    else {
+        seed->nSrc   = SSL_RSSRC_FILE;
+        seed->cpPath = ap_server_root_relative(mc->pPool, arg2);
+    }
+
+    if (seed->nSrc != SSL_RSSRC_BUILTIN) {
+        if (!seed->cpPath) {
+            return apr_pstrcat(cmd->pool,
+                               "Invalid SSLRandomSeed path ",
+                               arg2, NULL);
+        }
+        if (!ssl_util_path_check(SSL_PCM_EXISTS, seed->cpPath, cmd->pool)) {
+            return apr_pstrcat(cmd->pool,
+                               "SSLRandomSeed: source path '",
+                               seed->cpPath, "' does not exist", NULL);
+        }
+    }
+
+    if (!arg3) {
+        seed->nBytes = 0; /* read whole file */
+    }
+    else {
+        if (seed->nSrc == SSL_RSSRC_BUILTIN) {
+            return "SSLRandomSeed: byte specification not "
+                   "allowed for builtin seed source";
+        }
+
+        seed->nBytes = atoi(arg3);
+
+        if (seed->nBytes < 0) {
+            return "SSLRandomSeed: invalid number of bytes specified";
+        }
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    if (!strcasecmp(arg, "On")) {
+        sc->enabled = SSL_ENABLED_TRUE;
+        return NULL;
+    }
+    else if (!strcasecmp(arg, "Off")) {
+        sc->enabled = SSL_ENABLED_FALSE;
+        return NULL;
+    }
+    else if (!strcasecmp(arg, "Optional")) {
+        sc->enabled = SSL_ENABLED_OPTIONAL;
+        return NULL;
+    }
+
+    return "Argument must be On, Off, or Optional";
+}
+
+const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef HAVE_FIPS
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+#endif
+    const char *err;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+#ifdef HAVE_FIPS
+    if ((sc->fips != UNSET) && (sc->fips != (BOOL)(flag ? TRUE : FALSE)))
+        return "Conflicting SSLFIPS options, cannot be both On and Off";
+    sc->fips = flag ? TRUE : FALSE;
+#else
+    if (flag)
+        return "SSLFIPS invalid, rebuild httpd and openssl compiled for FIPS";
+#endif
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd,
+                                   void *dcfg,
+                                   const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+    /* always disable null and export ciphers */
+    arg = apr_pstrcat(cmd->pool, "!aNULL:!eNULL:!EXP:", arg, NULL);
+
+    if (cmd->path) {
+        dc->szCipherSuite = arg;
+    }
+    else {
+        sc->server->auth.cipher_suite = arg;
+    }
+
+    return NULL;
+}
+
+#define SSL_FLAGS_CHECK_FILE \
+    (SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO)
+
+#define SSL_FLAGS_CHECK_DIR \
+    (SSL_PCM_EXISTS|SSL_PCM_ISDIR)
+
+static const char *ssl_cmd_check_file(cmd_parms *parms,
+                                      const char **file)
+{
+    const char *filepath = ap_server_root_relative(parms->pool, *file);
+
+    if (!filepath) {
+        return apr_pstrcat(parms->pool, parms->cmd->name,
+                           ": Invalid file path ", *file, NULL);
+    }
+    *file = filepath;
+
+    if (ssl_util_path_check(SSL_FLAGS_CHECK_FILE, *file, parms->pool)) {
+        return NULL;
+    }
+
+    return apr_pstrcat(parms->pool, parms->cmd->name,
+                       ": file '", *file,
+                       "' does not exist or is empty", NULL);
+
+}
+
+const char *ssl_cmd_SSLCompression(cmd_parms *cmd, void *dcfg, int flag)
+{
+#if !defined(OPENSSL_NO_COMP)
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+#ifndef SSL_OP_NO_COMPRESSION
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err)
+        return "This version of openssl does not support configuring "
+               "compression within <VirtualHost> sections.";
+#endif
+    sc->compression = flag ? TRUE : FALSE;
+    return NULL;
+#else
+    return "Setting Compression mode unsupported; not implemented by the SSL library";
+#endif
+}
+
+const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->cipher_server_pref = flag?TRUE:FALSE;
+    return NULL;
+#else
+    return "SSLHonorCipherOrder unsupported; not implemented by the SSL library";
+#endif
+}
+
+const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->insecure_reneg = flag?TRUE:FALSE;
+    return NULL;
+#else
+    return "The SSLInsecureRenegotiation directive is not available "
+        "with this SSL library";
+#endif
+}
+
+
+static const char *ssl_cmd_check_dir(cmd_parms *parms,
+                                     const char **dir)
+{
+    const char *dirpath = ap_server_root_relative(parms->pool, *dir);
+
+    if (!dirpath) {
+        return apr_pstrcat(parms->pool, parms->cmd->name,
+                           ": Invalid dir path ", *dir, NULL);
+    }
+    *dir = dirpath;
+
+    if (ssl_util_path_check(SSL_FLAGS_CHECK_DIR, *dir, parms->pool)) {
+        return NULL;
+    }
+
+    return apr_pstrcat(parms->pool, parms->cmd->name,
+                       ": directory '", *dir,
+                       "' does not exist", NULL);
+
+}
+
+const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd,
+                                       void *dcfg,
+                                       const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    *(const char **)apr_array_push(sc->server->pks->cert_files) =
+        apr_pstrdup(cmd->pool, arg);
+    
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd,
+                                          void *dcfg,
+                                          const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    *(const char **)apr_array_push(sc->server->pks->key_files) =
+        apr_pstrdup(cmd->pool, arg);
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *cmd,
+                                            void *dcfg,
+                                            const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_STARTUP, 0, cmd->server,
+                 APLOGNO(02559)
+                 "The SSLCertificateChainFile directive (%s:%d) is deprecated, "
+                 "SSLCertificateFile should be used instead",
+                 cmd->directive->filename, cmd->directive->line_num);
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->server->cert_chain = arg;
+
+    return NULL;
+}
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd,
+                                            void *dcfg,
+                                            const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->server->ticket_key->file_path = arg;
+
+    return NULL;
+}
+#endif
+
+#define NO_PER_DIR_SSL_CA \
+    "Your SSL library does not have support for per-directory CA"
+
+const char *ssl_cmd_SSLCACertificatePath(cmd_parms *cmd,
+                                         void *dcfg,
+                                         const char *arg)
+{
+    /*SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;*/
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+        return err;
+    }
+
+    if (cmd->path) {
+        return NO_PER_DIR_SSL_CA;
+    }
+
+    /* XXX: bring back per-dir */
+    sc->server->auth.ca_cert_path = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCACertificateFile(cmd_parms *cmd,
+                                         void *dcfg,
+                                         const char *arg)
+{
+    /*SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;*/
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    if (cmd->path) {
+        return NO_PER_DIR_SSL_CA;
+    }
+
+    /* XXX: bring back per-dir */
+    sc->server->auth.ca_cert_file = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCADNRequestPath(cmd_parms *cmd, void *dcfg,
+                                       const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+        return err;
+    }
+
+    sc->server->pks->ca_name_path = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCADNRequestFile(cmd_parms *cmd, void *dcfg,
+                                       const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->server->pks->ca_name_file = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationPath(cmd_parms *cmd,
+                                        void *dcfg,
+                                        const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+        return err;
+    }
+
+    sc->server->crl_path = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationFile(cmd_parms *cmd,
+                                        void *dcfg,
+                                        const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->server->crl_file = arg;
+
+    return NULL;
+}
+
+static const char *ssl_cmd_crlcheck_parse(cmd_parms *parms,
+                                          const char *arg,
+                                          ssl_crlcheck_t *mode)
+{
+    if (strcEQ(arg, "none")) {
+        *mode = SSL_CRLCHECK_NONE;
+    }
+    else if (strcEQ(arg, "leaf")) {
+        *mode = SSL_CRLCHECK_LEAF;
+    }
+    else if (strcEQ(arg, "chain")) {
+        *mode = SSL_CRLCHECK_CHAIN;
+    }
+    else {
+        return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+                           ": Invalid argument '", arg, "'",
+                           NULL);
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationCheck(cmd_parms *cmd,
+                                         void *dcfg,
+                                         const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    return ssl_cmd_crlcheck_parse(cmd, arg, &sc->server->crl_check_mode);
+}
+
+static const char *ssl_cmd_verify_parse(cmd_parms *parms,
+                                        const char *arg,
+                                        ssl_verify_t *id)
+{
+    if (strcEQ(arg, "none") || strcEQ(arg, "off")) {
+        *id = SSL_CVERIFY_NONE;
+    }
+    else if (strcEQ(arg, "optional")) {
+        *id = SSL_CVERIFY_OPTIONAL;
+    }
+    else if (strcEQ(arg, "require") || strcEQ(arg, "on")) {
+        *id = SSL_CVERIFY_REQUIRE;
+    }
+    else if (strcEQ(arg, "optional_no_ca")) {
+        *id = SSL_CVERIFY_OPTIONAL_NO_CA;
+    }
+    else {
+        return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+                           ": Invalid argument '", arg, "'",
+                           NULL);
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLVerifyClient(cmd_parms *cmd,
+                                    void *dcfg,
+                                    const char *arg)
+{
+    SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    ssl_verify_t mode;
+    const char *err;
+
+    if ((err = ssl_cmd_verify_parse(cmd, arg, &mode))) {
+        return err;
+    }
+
+    if (cmd->path) {
+        dc->nVerifyClient = mode;
+    }
+    else {
+        sc->server->auth.verify_mode = mode;
+    }
+
+    return NULL;
+}
+
+static const char *ssl_cmd_verify_depth_parse(cmd_parms *parms,
+                                              const char *arg,
+                                              int *depth)
+{
+    if ((*depth = atoi(arg)) >= 0) {
+        return NULL;
+    }
+
+    return apr_pstrcat(parms->temp_pool, parms->cmd->name,
+                       ": Invalid argument '", arg, "'",
+                       NULL);
+}
+
+const char *ssl_cmd_SSLVerifyDepth(cmd_parms *cmd,
+                                   void *dcfg,
+                                   const char *arg)
+{
+    SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    int depth;
+    const char *err;
+
+    if ((err = ssl_cmd_verify_depth_parse(cmd, arg, &depth))) {
+        return err;
+    }
+
+    if (cmd->path) {
+        dc->nVerifyDepth = depth;
+    }
+    else {
+        sc->server->auth.verify_depth = depth;
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCache(cmd_parms *cmd,
+                                    void *dcfg,
+                                    const char *arg)
+{
+    SSLModConfigRec *mc = myModConfig(cmd->server);
+    const char *err, *sep, *name;
+    long enabled_flags;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    /* The OpenSSL session cache mode must have both the flags
+     * SSL_SESS_CACHE_SERVER and SSL_SESS_CACHE_NO_INTERNAL set if a
+     * session cache is configured; NO_INTERNAL prevents the
+     * OpenSSL-internal session cache being used in addition to the
+     * "external" (mod_ssl-provided) cache, which otherwise causes
+     * additional memory consumption. */
+    enabled_flags = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL;
+
+    if (strcEQ(arg, "none")) {
+        /* Nothing to do; session cache will be off. */
+    }
+    else if (strcEQ(arg, "nonenotnull")) {
+        /* ### Having a separate mode for this seems logically
+         * unnecessary; the stated purpose of sending non-empty
+         * session IDs would be better fixed in OpenSSL or simply
+         * doing it by default if "none" is used. */
+        mc->sesscache_mode = enabled_flags;
+    }
+    else {
+        /* Argument is of form 'name:args' or just 'name'. */
+        sep = ap_strchr_c(arg, ':');
+        if (sep) {
+            name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
+            sep++;
+        }
+        else {
+            name = arg;
+        }
+
+        /* Find the provider of given name. */
+        mc->sesscache = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
+                                           name,
+                                           AP_SOCACHE_PROVIDER_VERSION);
+        if (mc->sesscache) {
+            /* Cache found; create it, passing anything beyond the colon. */
+            mc->sesscache_mode = enabled_flags;
+            err = mc->sesscache->create(&mc->sesscache_context, sep,
+                                        cmd->temp_pool, cmd->pool);
+        }
+        else {
+            apr_array_header_t *name_list;
+            const char *all_names;
+
+            /* Build a comma-separated list of all registered provider
+             * names: */
+            name_list = ap_list_provider_names(cmd->pool,
+                                               AP_SOCACHE_PROVIDER_GROUP,
+                                               AP_SOCACHE_PROVIDER_VERSION);
+            all_names = apr_array_pstrcat(cmd->pool, name_list, ',');
+
+            err = apr_psprintf(cmd->pool, "'%s' session cache not supported "
+                               "(known names: %s). Maybe you need to load the "
+                               "appropriate socache module (mod_socache_%s?).",
+                               name, all_names, name);
+        }
+    }
+
+    if (err) {
+        return apr_psprintf(cmd->pool, "SSLSessionCache: %s", err);
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *cmd,
+                                           void *dcfg,
+                                           const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->session_cache_timeout = atoi(arg);
+
+    if (sc->session_cache_timeout < 0) {
+        return "SSLSessionCacheTimeout: Invalid argument";
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOptions(cmd_parms *cmd,
+                               void *dcfg,
+                               const char *arg)
+{
+    SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+    ssl_opt_t opt;
+    int first = TRUE;
+    char action, *w;
+
+    while (*arg) {
+        w = ap_getword_conf(cmd->temp_pool, &arg);
+        action = NUL;
+
+        if ((*w == '+') || (*w == '-')) {
+            action = *(w++);
+        }
+        else if (first) {
+            dc->nOptions = SSL_OPT_NONE;
+            first = FALSE;
+        }
+
+        if (strcEQ(w, "StdEnvVars")) {
+            opt = SSL_OPT_STDENVVARS;
+        }
+        else if (strcEQ(w, "ExportCertData")) {
+            opt = SSL_OPT_EXPORTCERTDATA;
+        }
+        else if (strcEQ(w, "FakeBasicAuth")) {
+            opt = SSL_OPT_FAKEBASICAUTH;
+        }
+        else if (strcEQ(w, "StrictRequire")) {
+            opt = SSL_OPT_STRICTREQUIRE;
+        }
+        else if (strcEQ(w, "OptRenegotiate")) {
+            opt = SSL_OPT_OPTRENEGOTIATE;
+        }
+        else if (strcEQ(w, "LegacyDNStringFormat")) {
+            opt = SSL_OPT_LEGACYDNFORMAT;
+        }
+        else {
+            return apr_pstrcat(cmd->pool,
+                               "SSLOptions: Illegal option '", w, "'",
+                               NULL);
+        }
+
+        if (action == '-') {
+            dc->nOptionsAdd &= ~opt;
+            dc->nOptionsDel |=  opt;
+            dc->nOptions    &= ~opt;
+        }
+        else if (action == '+') {
+            dc->nOptionsAdd |=  opt;
+            dc->nOptionsDel &= ~opt;
+            dc->nOptions    |=  opt;
+        }
+        else {
+            dc->nOptions    = opt;
+            dc->nOptionsAdd = opt;
+            dc->nOptionsDel = SSL_OPT_NONE;
+        }
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLRequireSSL(cmd_parms *cmd, void *dcfg)
+{
+    SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+    dc->bSSLRequired = TRUE;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLRequire(cmd_parms *cmd,
+                               void *dcfg,
+                               const char *arg)
+{
+    SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+    ap_expr_info_t *info = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t));
+    ssl_require_t *require;
+    const char *errstring;
+
+    info->flags = AP_EXPR_FLAG_SSL_EXPR_COMPAT;
+    info->filename = cmd->directive->filename;
+    info->line_number = cmd->directive->line_num;
+    info->module_index = APLOG_MODULE_INDEX;
+    errstring = ap_expr_parse(cmd->pool, cmd->temp_pool, info, arg, NULL);
+    if (errstring) {
+        return apr_pstrcat(cmd->pool, "SSLRequire: ", errstring, NULL);
+    }
+
+    require = apr_array_push(dc->aRequirement);
+    require->cpExpr = apr_pstrdup(cmd->pool, arg);
+    require->mpExpr = info;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+    SSLDirConfigRec *dc = dcfg;
+    int val;
+
+    val = atoi(arg);
+    if (val < 0) {
+        return apr_pstrcat(cmd->pool, "Invalid size for SSLRenegBufferSize: ",
+                           arg, NULL);
+    }
+    dc->nRenegBufferSize = val;
+
+    return NULL;
+}
+
+static const char *ssl_cmd_protocol_parse(cmd_parms *parms,
+                                          const char *arg,
+                                          ssl_proto_t *options)
+{
+    ssl_proto_t thisopt;
+
+    *options = SSL_PROTOCOL_NONE;
+
+    while (*arg) {
+        char *w = ap_getword_conf(parms->temp_pool, &arg);
+        char action = '\0';
+
+        if ((*w == '+') || (*w == '-')) {
+            action = *(w++);
+        }
+
+        if (strcEQ(w, "SSLv2")) {
+            if (action == '-') {
+                continue;
+            }
+            else {
+                return "SSLProtocol: SSLv2 is no longer supported";
+            }
+        }
+        else if (strcEQ(w, "SSLv3")) {
+            thisopt = SSL_PROTOCOL_SSLV3;
+        }
+        else if (strcEQ(w, "TLSv1")) {
+            thisopt = SSL_PROTOCOL_TLSV1;
+        }
+#ifdef HAVE_TLSV1_X
+        else if (strcEQ(w, "TLSv1.1")) {
+            thisopt = SSL_PROTOCOL_TLSV1_1;
+        }
+        else if (strcEQ(w, "TLSv1.2")) {
+            thisopt = SSL_PROTOCOL_TLSV1_2;
+        }
+#endif
+        else if (strcEQ(w, "all")) {
+            thisopt = SSL_PROTOCOL_ALL;
+        }
+        else {
+            return apr_pstrcat(parms->temp_pool,
+                               parms->cmd->name,
+                               ": Illegal protocol '",
+                               w, "'", NULL);
+        }
+
+        if (action == '-') {
+            *options &= ~thisopt;
+        }
+        else if (action == '+') {
+            *options |= thisopt;
+        }
+        else {
+            *options = thisopt;
+        }
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProtocol(cmd_parms *cmd,
+                                void *dcfg,
+                                const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    return ssl_cmd_protocol_parse(cmd, arg, &sc->server->protocol);
+}
+
+const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->proxy_enabled = flag ? TRUE : FALSE;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyProtocol(cmd_parms *cmd,
+                                     void *dcfg,
+                                     const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    return ssl_cmd_protocol_parse(cmd, arg, &sc->proxy->protocol);
+}
+
+const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *cmd,
+                                        void *dcfg,
+                                        const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    /* always disable null and export ciphers */
+    arg = apr_pstrcat(cmd->pool, "!aNULL:!eNULL:!EXP:", arg, NULL);
+
+    sc->proxy->auth.cipher_suite = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerify(cmd_parms *cmd,
+                                   void *dcfg,
+                                   const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    ssl_verify_t mode;
+    const char *err;
+
+    if ((err = ssl_cmd_verify_parse(cmd, arg, &mode))) {
+        return err;
+    }
+
+    sc->proxy->auth.verify_mode = mode;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *cmd,
+                                        void *dcfg,
+                                        const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    int depth;
+    const char *err;
+
+    if ((err = ssl_cmd_verify_depth_parse(cmd, arg, &depth))) {
+        return err;
+    }
+
+    sc->proxy->auth.verify_depth = depth;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *cmd,
+                                              void *dcfg,
+                                              const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->proxy->auth.ca_cert_file = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *cmd,
+                                              void *dcfg,
+                                              const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+        return err;
+    }
+
+    sc->proxy->auth.ca_cert_path = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *cmd,
+                                             void *dcfg,
+                                             const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+        return err;
+    }
+
+    sc->proxy->crl_path = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCARevocationFile(cmd_parms *cmd,
+                                             void *dcfg,
+                                             const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->proxy->crl_file = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCARevocationCheck(cmd_parms *cmd,
+                                              void *dcfg,
+                                              const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    return ssl_cmd_crlcheck_parse(cmd, arg, &sc->proxy->crl_check_mode);
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *cmd,
+                                                   void *dcfg,
+                                                   const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->proxy->pkp->cert_file = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *cmd,
+                                                   void *dcfg,
+                                                   const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_dir(cmd, &arg))) {
+        return err;
+    }
+
+    sc->proxy->pkp->cert_path = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *cmd,
+                                                   void *dcfg,
+                                                   const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg))) {
+        return err;
+    }
+
+    sc->proxy->pkp->ca_cert_file = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLUserName(cmd_parms *cmd, void *dcfg,
+                                const char *arg)
+{
+    SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+    dc->szUserName = arg;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->server->ocsp_enabled = flag ? TRUE : FALSE;
+
+#ifdef OPENSSL_NO_OCSP
+    if (flag) {
+        return "OCSP support disabled in SSL library; cannot enable "
+            "OCSP validation";
+    }
+#endif
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->server->ocsp_force_default = flag ? TRUE : FALSE;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->server->ocsp_responder = arg;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPResponseTimeSkew(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->ocsp_resptime_skew = atoi(arg);
+    if (sc->server->ocsp_resptime_skew < 0) {
+        return "SSLOCSPResponseTimeSkew: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPResponseMaxAge(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->ocsp_resp_maxage = atoi(arg);
+    if (sc->server->ocsp_resp_maxage < 0) {
+        return "SSLOCSPResponseMaxAge: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPResponderTimeout(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->ocsp_responder_timeout = apr_time_from_sec(atoi(arg));
+    if (sc->server->ocsp_responder_timeout < 0) {
+        return "SSLOCSPResponderTimeout: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLOCSPUseRequestNonce(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->server->ocsp_use_request_nonce = flag ? TRUE : FALSE;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->proxy_ssl_check_peer_expire = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->proxy_ssl_check_peer_cn = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE;
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCheckPeerName(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->proxy_ssl_check_peer_name = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE;
+
+    return NULL;
+}
+
+const char  *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef HAVE_TLSEXT
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+    sc->strict_sni_vhost_check = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE;
+
+    return NULL;
+#else
+    return "SSLStrictSNIVHostCheck failed; OpenSSL is not built with support "
+           "for TLS extensions and SNI indication. Refer to the "
+           "documentation, and build a compatible version of OpenSSL.";
+#endif
+}
+
+#ifdef HAVE_OCSP_STAPLING
+
+const char *ssl_cmd_SSLStaplingCache(cmd_parms *cmd,
+                                    void *dcfg,
+                                    const char *arg)
+{
+    SSLModConfigRec *mc = myModConfig(cmd->server);
+    const char *err, *sep, *name;
+
+    if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
+        return err;
+    }
+
+    /* Argument is of form 'name:args' or just 'name'. */
+    sep = ap_strchr_c(arg, ':');
+    if (sep) {
+        name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
+        sep++;
+    }
+    else {
+        name = arg;
+    }
+
+    /* Find the provider of given name. */
+    mc->stapling_cache = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
+                                            name,
+                                            AP_SOCACHE_PROVIDER_VERSION);
+    if (mc->stapling_cache) {
+        /* Cache found; create it, passing anything beyond the colon. */
+        err = mc->stapling_cache->create(&mc->stapling_cache_context,
+                                         sep, cmd->temp_pool,
+                                         cmd->pool);
+    }
+    else {
+        apr_array_header_t *name_list;
+        const char *all_names;
+
+        /* Build a comma-separated list of all registered provider
+         * names: */
+        name_list = ap_list_provider_names(cmd->pool,
+                                           AP_SOCACHE_PROVIDER_GROUP,
+                                           AP_SOCACHE_PROVIDER_VERSION);
+        all_names = apr_array_pstrcat(cmd->pool, name_list, ',');
+
+        err = apr_psprintf(cmd->pool, "'%s' stapling cache not supported "
+                           "(known names: %s) Maybe you need to load the "
+                           "appropriate socache module (mod_socache_%s?)",
+                           name, all_names, name);
+    }
+
+    if (err) {
+        return apr_psprintf(cmd->pool, "SSLStaplingCache: %s", err);
+    }
+
+    return NULL;
+}
+
+const char *ssl_cmd_SSLUseStapling(cmd_parms *cmd, void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_enabled = flag ? TRUE : FALSE;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingResponseTimeSkew(cmd_parms *cmd, void *dcfg,
+                                                    const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_resptime_skew = atoi(arg);
+    if (sc->server->stapling_resptime_skew < 0) {
+        return "SSLStaplingResponseTimeSkew: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingResponseMaxAge(cmd_parms *cmd, void *dcfg,
+                                                    const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_resp_maxage = atoi(arg);
+    if (sc->server->stapling_resp_maxage < 0) {
+        return "SSLStaplingResponseMaxAge: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingStandardCacheTimeout(cmd_parms *cmd, void *dcfg,
+                                                    const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_cache_timeout = atoi(arg);
+    if (sc->server->stapling_cache_timeout < 0) {
+        return "SSLStaplingStandardCacheTimeout: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingErrorCacheTimeout(cmd_parms *cmd, void *dcfg,
+                                                 const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_errcache_timeout = atoi(arg);
+    if (sc->server->stapling_errcache_timeout < 0) {
+        return "SSLStaplingErrorCacheTimeout: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingReturnResponderErrors(cmd_parms *cmd,
+                                                     void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_return_errors = flag ? TRUE : FALSE;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingFakeTryLater(cmd_parms *cmd,
+                                            void *dcfg, int flag)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_fake_trylater = flag ? TRUE : FALSE;
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingResponderTimeout(cmd_parms *cmd, void *dcfg,
+                                                const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_responder_timeout = atoi(arg);
+    sc->server->stapling_responder_timeout *= APR_USEC_PER_SEC;
+    if (sc->server->stapling_responder_timeout < 0) {
+        return "SSLStaplingResponderTimeout: invalid argument";
+    }
+    return NULL;
+}
+
+const char *ssl_cmd_SSLStaplingForceURL(cmd_parms *cmd, void *dcfg,
+                                        const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    sc->server->stapling_force_url = arg;
+    return NULL;
+}
+
+#endif /* HAVE_OCSP_STAPLING */
+
+#ifdef HAVE_SSL_CONF_CMD
+const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg,
+                                      const char *arg1, const char *arg2)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config;
+    int value_type = SSL_CONF_cmd_value_type(cctx, arg1);
+    const char *err;
+    ssl_ctx_param_t *param;
+
+    if (value_type == SSL_CONF_TYPE_UNKNOWN) {
+        return apr_psprintf(cmd->pool,
+                            "'%s': invalid OpenSSL configuration command",
+                            arg1);
+    }
+
+    if (value_type == SSL_CONF_TYPE_FILE) {
+        if ((err = ssl_cmd_check_file(cmd, &arg2)))
+            return err;
+    }
+    else if (value_type == SSL_CONF_TYPE_DIR) {
+        if ((err = ssl_cmd_check_dir(cmd, &arg2)))
+            return err;
+    }
+
+    param = apr_array_push(sc->server->ssl_ctx_param);
+    param->name = arg1;
+    param->value = arg2;
+    return NULL;
+}
+#endif
+
+#if defined(HAVE_ALPN_NPN) || defined(HAVE_TLS_NPN)
+const char *ssl_cmd_SSLAlpnPreference(cmd_parms *cmd, void *dcfg,
+                                      const char *protocol)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    APR_ARRAY_PUSH(sc->server->ssl_alpn_pref, const char *) = protocol;
+    return NULL;
+}
+#endif
+
+#ifdef HAVE_SRP
+
+const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
+                                       const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    const char *err;
+
+    if ((err = ssl_cmd_check_file(cmd, &arg)))
+        return err;
+    /* SRP_VBASE_init takes char*, not const char*  */
+    sc->server->srp_vfile = apr_pstrdup(cmd->pool, arg);
+    return NULL;
+}
+
+const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg,
+                                          const char *arg)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+    /* SRP_VBASE_new takes char*, not const char*  */
+    sc->server->srp_unknown_user_seed = apr_pstrdup(cmd->pool, arg);
+    return NULL;
+}
+
+#endif /* HAVE_SRP */
+
+void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s)
+{
+    apr_file_t *out = NULL;
+    if (!ap_exists_config_define("DUMP_CERTS")) {
+        return;
+    }
+    apr_file_open_stdout(&out, pconf);
+    apr_file_printf(out, "Server certificates:\n");
+
+    /* Dump the filenames of all configured server certificates to
+     * stdout. */
+    while (s) {
+        SSLSrvConfigRec *sc = mySrvConfig(s);
+
+        if (sc && sc->server && sc->server->pks) {
+            modssl_pk_server_t *const pks = sc->server->pks;
+            int i;
+
+            for (i = 0; (i < pks->cert_files->nelts) &&
+                        APR_ARRAY_IDX(pks->cert_files, i, const char *);
+                 i++) {
+                apr_file_printf(out, "  %s\n",
+                                APR_ARRAY_IDX(pks->cert_files,
+                                              i, const char *));
+            }
+        }
+
+        s = s->next;
+    }
+
+}
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_init.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_init.c
new file mode 100644 (file)
index 0000000..7181aec
--- /dev/null
@@ -0,0 +1,1774 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_init.c
+ *  Initialization of Servers
+ */
+                             /* ``Recursive, adj.;
+                                  see Recursive.''
+                                        -- Unknown   */
+#include "ssl_private.h"
+#include "mpm_common.h"
+
+/*  _________________________________________________________________
+**
+**  Module Initialization
+**  _________________________________________________________________
+*/
+
+#ifdef HAVE_ECC
+#define KEYTYPES "RSA, DSA or ECC"
+#else 
+#define KEYTYPES "RSA or DSA"
+#endif
+
+/*
+ * Grab well-defined DH parameters from OpenSSL, see the get_rfc*
+ * functions in <openssl/bn.h> for all available primes.
+ */
+static DH *make_dh_params(BIGNUM *(*prime)(BIGNUM *), const char *gen)
+{
+    DH *dh = DH_new();
+
+    if (!dh) {
+        return NULL;
+    }
+    dh->p = prime(NULL);
+    BN_dec2bn(&dh->g, gen);
+    if (!dh->p || !dh->g) {
+        DH_free(dh);
+        return NULL;
+    }
+    return dh;
+}
+
+/* Storage and initialization for DH parameters. */
+static struct dhparam {
+    BIGNUM *(*const prime)(BIGNUM *); /* function to generate... */
+    DH *dh;                           /* ...this, used for keys.... */
+    const unsigned int min;           /* ...of length >= this. */
+} dhparams[] = {
+    { get_rfc3526_prime_8192, NULL, 6145 },
+    { get_rfc3526_prime_6144, NULL, 4097 },
+    { get_rfc3526_prime_4096, NULL, 3073 },
+    { get_rfc3526_prime_3072, NULL, 2049 },
+    { get_rfc3526_prime_2048, NULL, 1025 },
+    { get_rfc2409_prime_1024, NULL, 0 }
+};
+
+static void init_dh_params(void)
+{
+    unsigned n;
+
+    for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++)
+        dhparams[n].dh = make_dh_params(dhparams[n].prime, "2");
+}
+
+static void free_dh_params(void)
+{
+    unsigned n;
+
+    /* DH_free() is a noop for a NULL parameter, so these are harmless
+     * in the (unexpected) case where these variables are already
+     * NULL. */
+    for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++) {
+        DH_free(dhparams[n].dh);
+        dhparams[n].dh = NULL;
+    }
+}
+
+/* Hand out the same DH structure though once generated as we leak
+ * memory otherwise and freeing the structure up after use would be
+ * hard to track and in fact is not needed at all as it is safe to
+ * use the same parameters over and over again security wise (in
+ * contrast to the keys itself) and code safe as the returned structure
+ * is duplicated by OpenSSL anyway. Hence no modification happens
+ * to our copy. */
+DH *modssl_get_dh_params(unsigned keylen)
+{
+    unsigned n;
+
+    for (n = 0; n < sizeof(dhparams)/sizeof(dhparams[0]); n++)
+        if (keylen >= dhparams[n].min)
+            return dhparams[n].dh;
+        
+    return NULL; /* impossible to reach. */
+}
+
+static void ssl_add_version_components(apr_pool_t *p,
+                                       server_rec *s)
+{
+    char *modver = ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_INTERFACE");
+    char *libver = ssl_var_lookup(p, s, NULL, NULL, "SSL_VERSION_LIBRARY");
+    char *incver = ssl_var_lookup(p, s, NULL, NULL,
+                                  "SSL_VERSION_LIBRARY_INTERFACE");
+
+    ap_add_version_component(p, libver);
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01876)
+                 "%s compiled against Server: %s, Library: %s",
+                 modver, AP_SERVER_BASEVERSION, incver);
+}
+
+/*
+ *  Per-module initialization
+ */
+apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
+                             apr_pool_t *ptemp,
+                             server_rec *base_server)
+{
+    SSLModConfigRec *mc = myModConfig(base_server);
+    SSLSrvConfigRec *sc;
+    server_rec *s;
+    apr_status_t rv;
+    apr_array_header_t *pphrases;
+
+    if (SSLeay() < SSL_LIBRARY_VERSION) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(01882)
+                     "Init: this version of mod_ssl was compiled against "
+                     "a newer library (%s, version currently loaded is %s)"
+                     " - may result in undefined or erroneous behavior",
+                     SSL_LIBRARY_TEXT, SSLeay_version(SSLEAY_VERSION));
+    }
+
+    /* We initialize mc->pid per-process in the child init,
+     * but it should be initialized for startup before we
+     * call ssl_rand_seed() below.
+     */
+    mc->pid = getpid();
+
+    /*
+     * Let us cleanup on restarts and exits
+     */
+    apr_pool_cleanup_register(p, base_server,
+                              ssl_init_ModuleKill,
+                              apr_pool_cleanup_null);
+
+    /*
+     * Any init round fixes the global config
+     */
+    ssl_config_global_create(base_server); /* just to avoid problems */
+    ssl_config_global_fix(mc);
+
+    /*
+     *  try to fix the configuration and open the dedicated SSL
+     *  logfile as early as possible
+     */
+    for (s = base_server; s; s = s->next) {
+        sc = mySrvConfig(s);
+
+        if (sc->server) {
+            sc->server->sc = sc;
+        }
+
+        if (sc->proxy) {
+            sc->proxy->sc = sc;
+        }
+
+        /*
+         * Create the server host:port string because we need it a lot
+         */
+        sc->vhost_id = ssl_util_vhostid(p, s);
+        sc->vhost_id_len = strlen(sc->vhost_id);
+
+        /* Default to enabled if SSLEngine is not set explicitly, and
+         * the protocol is https. */
+        if (ap_get_server_protocol(s) 
+            && strcmp("https", ap_get_server_protocol(s)) == 0
+            && sc->enabled == SSL_ENABLED_UNSET) {
+            sc->enabled = SSL_ENABLED_TRUE;
+        }
+
+        /* Fix up stuff that may not have been set.  If sc->enabled is
+         * UNSET, then SSL is disabled on this vhost.  */
+        if (sc->enabled == SSL_ENABLED_UNSET) {
+            sc->enabled = SSL_ENABLED_FALSE;
+        }
+        if (sc->proxy_enabled == UNSET) {
+            sc->proxy_enabled = FALSE;
+        }
+
+        if (sc->session_cache_timeout == UNSET) {
+            sc->session_cache_timeout = SSL_SESSION_CACHE_TIMEOUT;
+        }
+
+        if (sc->server && sc->server->pphrase_dialog_type == SSL_PPTYPE_UNSET) {
+            sc->server->pphrase_dialog_type = SSL_PPTYPE_BUILTIN;
+        }
+
+#ifdef HAVE_FIPS
+        if (sc->fips == UNSET) {
+            sc->fips = FALSE;
+        }
+#endif
+    }
+
+#if APR_HAS_THREADS
+    ssl_util_thread_setup(p);
+#endif
+
+    /*
+     * SSL external crypto device ("engine") support
+     */
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+    if ((rv = ssl_init_Engine(base_server, p)) != APR_SUCCESS) {
+        return rv;
+    }
+#endif
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01883)
+                 "Init: Initialized %s library", SSL_LIBRARY_NAME);
+
+    /*
+     * Seed the Pseudo Random Number Generator (PRNG)
+     * only need ptemp here; nothing inside allocated from the pool
+     * needs to live once we return from ssl_rand_seed().
+     */
+    ssl_rand_seed(base_server, ptemp, SSL_RSCTX_STARTUP, "Init: ");
+
+#ifdef HAVE_FIPS
+    if(sc->fips) {
+        if (!FIPS_mode()) {
+            if (FIPS_mode_set(1)) {
+                ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(01884)
+                             "Operating in SSL FIPS mode");
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01885) "FIPS mode failed");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return ssl_die(s);
+            }
+        }
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01886)
+                     "SSL FIPS mode disabled");
+    }
+#endif
+
+    /*
+     * initialize the mutex handling
+     */
+    if (!ssl_mutex_init(base_server, p)) {
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+#ifdef HAVE_OCSP_STAPLING
+    ssl_stapling_ex_init();
+#endif
+
+    /*
+     * initialize session caching
+     */
+    if ((rv = ssl_scache_init(base_server, p)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    pphrases = apr_array_make(ptemp, 2, sizeof(char *));
+
+    /*
+     *  initialize servers
+     */
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server, APLOGNO(01887)
+                 "Init: Initializing (virtual) servers for SSL");
+
+    for (s = base_server; s; s = s->next) {
+        sc = mySrvConfig(s);
+        /*
+         * Either now skip this server when SSL is disabled for
+         * it or give out some information about what we're
+         * configuring.
+         */
+
+        /*
+         * Read the server certificate and key
+         */
+        if ((rv = ssl_init_ConfigureServer(s, p, ptemp, sc, pphrases))
+            != APR_SUCCESS) {
+            return rv;
+        }
+    }
+
+    if (pphrases->nelts > 0) {
+        memset(pphrases->elts, 0, pphrases->elt_size * pphrases->nelts);
+        pphrases->nelts = 0;
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02560)
+                     "Init: Wiped out the queried pass phrases from memory");
+    }
+
+    /*
+     * Configuration consistency checks
+     */
+    if ((rv = ssl_init_CheckServers(base_server, ptemp)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    /*
+     *  Announce mod_ssl and SSL library in HTTP Server field
+     *  as ``mod_ssl/X.X.X OpenSSL/X.X.X''
+     */
+    ssl_add_version_components(p, base_server);
+
+    SSL_init_app_data2_idx(); /* for SSL_get_app_data2() at request time */
+
+    init_dh_params();
+
+    return OK;
+}
+
+/*
+ * Support for external a Crypto Device ("engine"), usually
+ * a hardware accellerator card for crypto operations.
+ */
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+apr_status_t ssl_init_Engine(server_rec *s, apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    ENGINE *e;
+
+    if (mc->szCryptoDevice) {
+        if (!(e = ENGINE_by_id(mc->szCryptoDevice))) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01888)
+                         "Init: Failed to load Crypto Device API `%s'",
+                         mc->szCryptoDevice);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+            return ssl_die(s);
+        }
+
+        if (strEQ(mc->szCryptoDevice, "chil")) {
+            ENGINE_ctrl(e, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0);
+        }
+
+        if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01889)
+                         "Init: Failed to enable Crypto Device API `%s'",
+                         mc->szCryptoDevice);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+            return ssl_die(s);
+        }
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01890)
+                     "Init: loaded Crypto Device API `%s'",
+                     mc->szCryptoDevice);
+
+        ENGINE_free(e);
+    }
+
+    return APR_SUCCESS;
+}
+#endif
+
+#ifdef HAVE_TLSEXT
+static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
+                                                apr_pool_t *p,
+                                                apr_pool_t *ptemp,
+                                                modssl_ctx_t *mctx)
+{
+    apr_status_t rv;
+
+    /*
+     * Configure TLS extensions support
+     */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01893)
+                 "Configuring TLS extension handling");
+
+    /*
+     * Server name indication (SNI)
+     */
+    if (!SSL_CTX_set_tlsext_servername_callback(mctx->ssl_ctx,
+                          ssl_callback_ServerNameIndication) ||
+        !SSL_CTX_set_tlsext_servername_arg(mctx->ssl_ctx, mctx)) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01894)
+                     "Unable to initialize TLS servername extension "
+                     "callback (incompatible OpenSSL version?)");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+#ifdef HAVE_OCSP_STAPLING
+    /*
+     * OCSP Stapling support, status_request extension
+     */
+    if ((mctx->pkp == FALSE) && (mctx->stapling_enabled == TRUE)) {
+        if ((rv = modssl_init_stapling(s, p, ptemp, mctx)) != APR_SUCCESS) {
+            return rv;
+        }
+    }
+#endif
+
+#ifdef HAVE_SRP
+    /*
+     * TLS-SRP support
+     */
+    if (mctx->srp_vfile != NULL) {
+        int err;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02308)
+                     "Using SRP verifier file [%s]", mctx->srp_vfile);
+
+        if (!(mctx->srp_vbase = SRP_VBASE_new(mctx->srp_unknown_user_seed))) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02309)
+                         "Unable to initialize SRP verifier structure "
+                         "[%s seed]",
+                         mctx->srp_unknown_user_seed ? "with" : "without");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+            return ssl_die(s);
+        }
+
+        err = SRP_VBASE_init(mctx->srp_vbase, mctx->srp_vfile);
+        if (err != SRP_NO_ERROR) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02310)
+                         "Unable to load SRP verifier file [error %d]", err);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+            return ssl_die(s);
+        }
+
+        SSL_CTX_set_srp_username_callback(mctx->ssl_ctx,
+                                          ssl_callback_SRPServerParams);
+        SSL_CTX_set_srp_cb_arg(mctx->ssl_ctx, mctx);
+    }
+#endif
+    return APR_SUCCESS;
+}
+#endif
+
+static apr_status_t ssl_init_ctx_protocol(server_rec *s,
+                                          apr_pool_t *p,
+                                          apr_pool_t *ptemp,
+                                          modssl_ctx_t *mctx)
+{
+    SSL_CTX *ctx = NULL;
+    MODSSL_SSL_METHOD_CONST SSL_METHOD *method = NULL;
+    char *cp;
+    int protocol = mctx->protocol;
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+
+    /*
+     *  Create the new per-server SSL context
+     */
+    if (protocol == SSL_PROTOCOL_NONE) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02231)
+                "No SSL protocols available [hint: SSLProtocol]");
+        return ssl_die(s);
+    }
+
+    cp = apr_pstrcat(p,
+                     (protocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""),
+                     (protocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""),
+#ifdef HAVE_TLSV1_X
+                     (protocol & SSL_PROTOCOL_TLSV1_1 ? "TLSv1.1, " : ""),
+                     (protocol & SSL_PROTOCOL_TLSV1_2 ? "TLSv1.2, " : ""),
+#endif
+                     NULL);
+    cp[strlen(cp)-2] = NUL;
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
+                 "Creating new SSL context (protocols: %s)", cp);
+
+    if (protocol == SSL_PROTOCOL_SSLV3) {
+        method = mctx->pkp ?
+            SSLv3_client_method() : /* proxy */
+            SSLv3_server_method();  /* server */
+    }
+    else if (protocol == SSL_PROTOCOL_TLSV1) {
+        method = mctx->pkp ?
+            TLSv1_client_method() : /* proxy */
+            TLSv1_server_method();  /* server */
+    }
+#ifdef HAVE_TLSV1_X
+    else if (protocol == SSL_PROTOCOL_TLSV1_1) {
+        method = mctx->pkp ?
+            TLSv1_1_client_method() : /* proxy */
+            TLSv1_1_server_method();  /* server */
+    }
+    else if (protocol == SSL_PROTOCOL_TLSV1_2) {
+        method = mctx->pkp ?
+            TLSv1_2_client_method() : /* proxy */
+            TLSv1_2_server_method();  /* server */
+    }
+#endif
+    else { /* For multiple protocols, we need a flexible method */
+        method = mctx->pkp ?
+            SSLv23_client_method() : /* proxy */
+            SSLv23_server_method();  /* server */
+    }
+    ctx = SSL_CTX_new(method);
+
+    mctx->ssl_ctx = ctx;
+
+    SSL_CTX_set_options(ctx, SSL_OP_ALL);
+
+    /* always disable SSLv2, as per RFC 6176 */
+    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+
+    if (!(protocol & SSL_PROTOCOL_SSLV3)) {
+        SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+    }
+
+    if (!(protocol & SSL_PROTOCOL_TLSV1)) {
+        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
+    }
+
+#ifdef HAVE_TLSV1_X
+    if (!(protocol & SSL_PROTOCOL_TLSV1_1)) {
+        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
+    }
+
+    if (!(protocol & SSL_PROTOCOL_TLSV1_2)) {
+        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
+    }
+#endif
+
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+    if (sc->cipher_server_pref == TRUE) {
+        SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+    }
+#endif
+
+
+#ifndef OPENSSL_NO_COMP
+    if (sc->compression != TRUE) {
+#ifdef SSL_OP_NO_COMPRESSION
+        /* OpenSSL >= 1.0 only */
+        SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
+#else
+        sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
+#endif
+    }
+#endif
+
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+    if (sc->insecure_reneg == TRUE) {
+        SSL_CTX_set_options(ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+    }
+#endif
+
+    SSL_CTX_set_app_data(ctx, s);
+
+    /*
+     * Configure additional context ingredients
+     */
+    SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
+#ifdef HAVE_ECC
+    SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
+#endif
+
+#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+    /*
+     * Disallow a session from being resumed during a renegotiation,
+     * so that an acceptable cipher suite can be negotiated.
+     */
+    SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+#endif
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+    /* If httpd is configured to reduce mem usage, ask openssl to do so, too */
+    if (ap_max_mem_free != APR_ALLOCATOR_MAX_FREE_UNLIMITED)
+        SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+    return APR_SUCCESS;
+}
+
+static void ssl_init_ctx_session_cache(server_rec *s,
+                                       apr_pool_t *p,
+                                       apr_pool_t *ptemp,
+                                       modssl_ctx_t *mctx)
+{
+    SSL_CTX *ctx = mctx->ssl_ctx;
+    SSLModConfigRec *mc = myModConfig(s);
+
+    SSL_CTX_set_session_cache_mode(ctx, mc->sesscache_mode);
+
+    if (mc->sesscache) {
+        SSL_CTX_sess_set_new_cb(ctx,    ssl_callback_NewSessionCacheEntry);
+        SSL_CTX_sess_set_get_cb(ctx,    ssl_callback_GetSessionCacheEntry);
+        SSL_CTX_sess_set_remove_cb(ctx, ssl_callback_DelSessionCacheEntry);
+    }
+}
+
+static void ssl_init_ctx_callbacks(server_rec *s,
+                                   apr_pool_t *p,
+                                   apr_pool_t *ptemp,
+                                   modssl_ctx_t *mctx)
+{
+    SSL_CTX *ctx = mctx->ssl_ctx;
+
+    SSL_CTX_set_tmp_dh_callback(ctx,  ssl_callback_TmpDH);
+
+    SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
+
+#if defined(HAVE_TLS_ALPN)
+       SSL_CTX_set_alpn_select_cb(
+          ctx, ssl_callback_alpn_select, NULL);
+#elif defined(HAVE_TLS_NPN)
+    SSL_CTX_set_next_protos_advertised_cb(
+        ctx, ssl_callback_AdvertiseNextProtos, NULL);
+#endif
+}
+
+static apr_status_t ssl_init_ctx_verify(server_rec *s,
+                                        apr_pool_t *p,
+                                        apr_pool_t *ptemp,
+                                        modssl_ctx_t *mctx)
+{
+    SSL_CTX *ctx = mctx->ssl_ctx;
+
+    int verify = SSL_VERIFY_NONE;
+    STACK_OF(X509_NAME) *ca_list;
+
+    if (mctx->auth.verify_mode == SSL_CVERIFY_UNSET) {
+        mctx->auth.verify_mode = SSL_CVERIFY_NONE;
+    }
+
+    if (mctx->auth.verify_depth == UNSET) {
+        mctx->auth.verify_depth = 1;
+    }
+
+    /*
+     *  Configure callbacks for SSL context
+     */
+    if (mctx->auth.verify_mode == SSL_CVERIFY_REQUIRE) {
+        verify |= SSL_VERIFY_PEER_STRICT;
+    }
+
+    if ((mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
+        (mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+    {
+        verify |= SSL_VERIFY_PEER;
+    }
+
+    SSL_CTX_set_verify(ctx, verify, ssl_callback_SSLVerify);
+
+    /*
+     * Configure Client Authentication details
+     */
+    if (mctx->auth.ca_cert_file || mctx->auth.ca_cert_path) {
+        ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
+                     "Configuring client authentication");
+
+        if (!SSL_CTX_load_verify_locations(ctx,
+                                           mctx->auth.ca_cert_file,
+                                           mctx->auth.ca_cert_path))
+        {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01895)
+                    "Unable to configure verify locations "
+                    "for client authentication");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+            return ssl_die(s);
+        }
+
+        if (mctx->pks && (mctx->pks->ca_name_file || mctx->pks->ca_name_path)) {
+            ca_list = ssl_init_FindCAList(s, ptemp,
+                                          mctx->pks->ca_name_file,
+                                          mctx->pks->ca_name_path);
+        } else
+            ca_list = ssl_init_FindCAList(s, ptemp,
+                                          mctx->auth.ca_cert_file,
+                                          mctx->auth.ca_cert_path);
+        if (sk_X509_NAME_num(ca_list) <= 0) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01896)
+                    "Unable to determine list of acceptable "
+                    "CA certificates for client authentication");
+            return ssl_die(s);
+        }
+
+        SSL_CTX_set_client_CA_list(ctx, ca_list);
+    }
+
+    /*
+     * Give a warning when no CAs were configured but client authentication
+     * should take place. This cannot work.
+     */
+    if (mctx->auth.verify_mode == SSL_CVERIFY_REQUIRE) {
+        ca_list = SSL_CTX_get_client_CA_list(ctx);
+
+        if (sk_X509_NAME_num(ca_list) == 0) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01897)
+                         "Init: Oops, you want to request client "
+                         "authentication, but no CAs are known for "
+                         "verification!?  [Hint: SSLCACertificate*]");
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_ctx_cipher_suite(server_rec *s,
+                                              apr_pool_t *p,
+                                              apr_pool_t *ptemp,
+                                              modssl_ctx_t *mctx)
+{
+    SSL_CTX *ctx = mctx->ssl_ctx;
+    const char *suite;
+
+    /*
+     *  Configure SSL Cipher Suite. Always disable NULL and export ciphers,
+     *  see also ssl_engine_config.c:ssl_cmd_SSLCipherSuite().
+     *  OpenSSL's SSL_DEFAULT_CIPHER_LIST already includes !aNULL:!eNULL,
+     *  so only prepend !EXP in this case.
+     */
+    suite = mctx->auth.cipher_suite ? mctx->auth.cipher_suite :
+            apr_pstrcat(ptemp, "!EXP:", SSL_DEFAULT_CIPHER_LIST, NULL);
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s,
+                 "Configuring permitted SSL ciphers [%s]",
+                 suite);
+
+    if (!SSL_CTX_set_cipher_list(ctx, suite)) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01898)
+                "Unable to configure permitted SSL ciphers");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_ctx_crl(server_rec *s,
+                                     apr_pool_t *p,
+                                     apr_pool_t *ptemp,
+                                     modssl_ctx_t *mctx)
+{
+    X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+    unsigned long crlflags = 0;
+    char *cfgp = mctx->pkp ? "SSLProxy" : "SSL";
+
+    /*
+     * Configure Certificate Revocation List (CRL) Details
+     */
+
+    if (!(mctx->crl_file || mctx->crl_path)) {
+        if (mctx->crl_check_mode == SSL_CRLCHECK_LEAF ||
+            mctx->crl_check_mode == SSL_CRLCHECK_CHAIN) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01899)
+                         "Host %s: CRL checking has been enabled, but "
+                         "neither %sCARevocationFile nor %sCARevocationPath "
+                         "is configured", mctx->sc->vhost_id, cfgp, cfgp);
+            return ssl_die(s);
+        }
+        return APR_SUCCESS;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01900)
+                 "Configuring certificate revocation facility");
+
+    if (!store || !X509_STORE_load_locations(store, mctx->crl_file,
+                                             mctx->crl_path)) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01901)
+                     "Host %s: unable to configure X.509 CRL storage "
+                     "for certificate revocation", mctx->sc->vhost_id);
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    switch (mctx->crl_check_mode) {
+       case SSL_CRLCHECK_LEAF:
+           crlflags = X509_V_FLAG_CRL_CHECK;
+           break;
+       case SSL_CRLCHECK_CHAIN:
+           crlflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;
+           break;
+       default:
+           crlflags = 0;
+    }
+
+    if (crlflags) {
+        X509_STORE_set_flags(store, crlflags);
+    } else {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01902)
+                     "Host %s: X.509 CRL storage locations configured, "
+                     "but CRL checking (%sCARevocationCheck) is not "
+                     "enabled", mctx->sc->vhost_id, cfgp);
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_ctx_cert_chain(server_rec *s,
+                                            apr_pool_t *p,
+                                            apr_pool_t *ptemp,
+                                            modssl_ctx_t *mctx)
+{
+    BOOL skip_first = FALSE;
+    int i, n;
+    const char *chain = mctx->cert_chain;
+
+    /*
+     * Optionally configure extra server certificate chain certificates.
+     * This is usually done by OpenSSL automatically when one of the
+     * server cert issuers are found under SSLCACertificatePath or in
+     * SSLCACertificateFile. But because these are intended for client
+     * authentication it can conflict. For instance when you use a
+     * Global ID server certificate you've to send out the intermediate
+     * CA certificate, too. When you would just configure this with
+     * SSLCACertificateFile and also use client authentication mod_ssl
+     * would accept all clients also issued by this CA. Obviously this
+     * isn't what we want in this situation. So this feature here exists
+     * to allow one to explicity configure CA certificates which are
+     * used only for the server certificate chain.
+     */
+    if (!chain) {
+        return APR_SUCCESS;
+    }
+
+    for (i = 0; (i < mctx->pks->cert_files->nelts) &&
+         APR_ARRAY_IDX(mctx->pks->cert_files, i, const char *); i++) {
+        if (strEQ(APR_ARRAY_IDX(mctx->pks->cert_files, i, const char *), chain)) {
+            skip_first = TRUE;
+            break;
+        }
+    }
+
+    n = SSL_CTX_use_certificate_chain(mctx->ssl_ctx,
+                                      (char *)chain,
+                                      skip_first, NULL);
+    if (n < 0) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01903)
+                "Failed to configure CA certificate chain!");
+        return ssl_die(s);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01904)
+                 "Configuring server certificate chain "
+                 "(%d CA certificate%s)",
+                 n, n == 1 ? "" : "s");
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_ctx(server_rec *s,
+                                 apr_pool_t *p,
+                                 apr_pool_t *ptemp,
+                                 modssl_ctx_t *mctx)
+{
+    apr_status_t rv;
+
+    if ((rv = ssl_init_ctx_protocol(s, p, ptemp, mctx)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    ssl_init_ctx_session_cache(s, p, ptemp, mctx);
+
+    ssl_init_ctx_callbacks(s, p, ptemp, mctx);
+
+    if ((rv = ssl_init_ctx_verify(s, p, ptemp, mctx)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    if ((rv = ssl_init_ctx_cipher_suite(s, p, ptemp, mctx)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    if ((rv = ssl_init_ctx_crl(s, p, ptemp, mctx)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    if (mctx->pks) {
+        /* XXX: proxy support? */
+        if ((rv = ssl_init_ctx_cert_chain(s, p, ptemp, mctx)) != APR_SUCCESS) {
+            return rv;
+        }
+#ifdef HAVE_TLSEXT
+        if ((rv = ssl_init_ctx_tls_extensions(s, p, ptemp, mctx)) !=
+            APR_SUCCESS) {
+            return rv;
+        }
+#endif
+    }
+
+    return APR_SUCCESS;
+}
+
+static void ssl_check_public_cert(server_rec *s,
+                                  apr_pool_t *ptemp,
+                                  X509 *cert,
+                                  const char *key_id)
+{
+    int is_ca, pathlen;
+
+    if (!cert) {
+        return;
+    }
+
+    /*
+     * Some information about the certificate(s)
+     */
+
+    if (SSL_X509_getBC(cert, &is_ca, &pathlen)) {
+        if (is_ca) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01906)
+                         "%s server certificate is a CA certificate "
+                         "(BasicConstraints: CA == TRUE !?)", key_id);
+        }
+
+        if (pathlen > 0) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01907)
+                         "%s server certificate is not a leaf certificate "
+                         "(BasicConstraints: pathlen == %d > 0 !?)",
+                         key_id, pathlen);
+        }
+    }
+
+    if (SSL_X509_match_name(ptemp, cert, (const char *)s->server_hostname,
+                            TRUE, s) == FALSE) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01909)
+                     "%s server certificate does NOT include an ID "
+                     "which matches the server name", key_id);
+    }
+}
+
+/* prevent OpenSSL from showing its "Enter PEM pass phrase:" prompt */
+static int ssl_no_passwd_prompt_cb(char *buf, int size, int rwflag,
+                                   void *userdata) {
+   return 0;
+}
+
+static apr_status_t ssl_init_server_certs(server_rec *s,
+                                          apr_pool_t *p,
+                                          apr_pool_t *ptemp,
+                                          modssl_ctx_t *mctx,
+                                          apr_array_header_t *pphrases)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    const char *vhost_id = mctx->sc->vhost_id, *key_id, *certfile, *keyfile;
+    int i;
+    X509 *cert;
+    DH *dhparams;
+#ifdef HAVE_ECC
+    EC_GROUP *ecparams;
+    int nid;
+    EC_KEY *eckey;
+#endif
+#ifndef HAVE_SSL_CONF_CMD
+    SSL *ssl;
+#endif
+
+    /* no OpenSSL default prompts for any of the SSL_CTX_use_* calls, please */
+    SSL_CTX_set_default_passwd_cb(mctx->ssl_ctx, ssl_no_passwd_prompt_cb);
+
+    /* Iterate over the SSLCertificateFile array */
+    for (i = 0; (i < mctx->pks->cert_files->nelts) &&
+                (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
+                                          const char *));
+         i++) {
+        key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
+
+        ERR_clear_error();
+
+        /* first the certificate (public key) */
+        if (mctx->cert_chain) {
+            if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
+                                              SSL_FILETYPE_PEM) < 1)) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
+                             "Failed to configure certificate %s, check %s",
+                             key_id, certfile);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return APR_EGENERAL;
+            }
+        } else {
+            if ((SSL_CTX_use_certificate_chain_file(mctx->ssl_ctx,
+                                                    certfile) < 1)) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02562)
+                             "Failed to configure certificate %s (with chain),"
+                             " check %s", key_id, certfile);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return APR_EGENERAL;
+            }
+        }
+
+        /* and second, the private key */
+        if (i < mctx->pks->key_files->nelts) {
+            keyfile = APR_ARRAY_IDX(mctx->pks->key_files, i, const char *);
+        } else {
+            keyfile = certfile;
+        }
+
+        ERR_clear_error();
+
+        if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
+                                         SSL_FILETYPE_PEM) < 1) &&
+            (ERR_GET_FUNC(ERR_peek_last_error())
+                != X509_F_X509_CHECK_PRIVATE_KEY)) {
+            ssl_asn1_t *asn1;
+            EVP_PKEY *pkey;
+            const unsigned char *ptr;
+
+            ERR_clear_error();
+
+            /* perhaps it's an encrypted private key, so try again */
+            ssl_load_encrypted_pkey(s, ptemp, i, keyfile, &pphrases);
+
+            if (!(asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id)) ||
+                !(ptr = asn1->cpData) ||
+                !(pkey = d2i_AutoPrivateKey(NULL, &ptr, asn1->nData)) ||
+                (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1)) {
+                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02564)
+                             "Failed to configure encrypted (?) private key %s,"
+                             " check %s", key_id, keyfile);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+                return APR_EGENERAL;
+            }
+        }
+
+        if (SSL_CTX_check_private_key(mctx->ssl_ctx) < 1) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02565)
+                         "Certificate and private key %s from %s and %s "
+                         "do not match", key_id, certfile, keyfile);
+            return APR_EGENERAL;
+        }
+
+#ifdef HAVE_SSL_CONF_CMD
+        /* 
+         * workaround for those OpenSSL versions where SSL_CTX_get0_certificate
+         * is not yet available: create an SSL struct which we dispose of
+         * as soon as we no longer need access to the cert. (Strictly speaking,
+         * SSL_CTX_get0_certificate does not depend on the SSL_CONF stuff,
+         * but there's no reliable way to check for its existence, so we
+         * assume that if SSL_CONF is available, it's OpenSSL 1.0.2 or later,
+         * and SSL_CTX_get0_certificate is implemented.)
+         */
+        if (!(cert = SSL_CTX_get0_certificate(mctx->ssl_ctx))) {
+#else
+        ssl = SSL_new(mctx->ssl_ctx);
+       if (ssl) {
+            /* Workaround bug in SSL_get_certificate in OpenSSL 0.9.8y */
+            SSL_set_connect_state(ssl);
+            cert = SSL_get_certificate(ssl);
+        }
+        if (!ssl || !cert) {
+#endif
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02566)
+                         "Unable to retrieve certificate %s", key_id);
+#ifndef HAVE_SSL_CONF_CMD
+            if (ssl)
+                SSL_free(ssl);
+#endif
+            return APR_EGENERAL;
+        }
+
+        /* warn about potential cert issues */
+        ssl_check_public_cert(s, ptemp, cert, key_id);
+
+#if defined(HAVE_OCSP_STAPLING) && !defined(SSL_CTRL_SET_CURRENT_CERT)
+        /* 
+         * OpenSSL up to 1.0.1: configure stapling as we go. In 1.0.2
+         * and later, there's SSL_CTX_set_current_cert, which allows
+         * iterating over all certs in an SSL_CTX (including those possibly
+         * loaded via SSLOpenSSLConfCmd Certificate), so for 1.0.2 and
+         * later, we defer to the code in ssl_init_server_ctx.
+         */
+        if ((mctx->stapling_enabled == TRUE) &&
+            !ssl_stapling_init_cert(s, mctx, cert)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02567)
+                         "Unable to configure certificate %s for stapling",
+                         key_id);
+        }
+#endif
+
+#ifndef HAVE_SSL_CONF_CMD
+        SSL_free(ssl);
+#endif
+
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02568)
+                     "Certificate and private key %s configured from %s and %s",
+                     key_id, certfile, keyfile);
+    }
+
+    /*
+     * Try to read DH parameters from the (first) SSLCertificateFile
+     */
+    if ((certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *)) &&
+        (dhparams = ssl_dh_GetParamFromFile(certfile))) {
+        SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
+                     "Custom DH parameters (%d bits) for %s loaded from %s",
+                     BN_num_bits(dhparams->p), vhost_id, certfile);
+    }
+
+#ifdef HAVE_ECC
+    /*
+     * Similarly, try to read the ECDH curve name from SSLCertificateFile...
+     */
+    if ((certfile != NULL) && 
+        (ecparams = ssl_ec_GetParamFromFile(certfile)) &&
+        (nid = EC_GROUP_get_curve_name(ecparams)) &&
+        (eckey = EC_KEY_new_by_curve_name(nid))) {
+        SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
+                     "ECDH curve %s for %s specified in %s",
+                     OBJ_nid2sn(nid), vhost_id, certfile);
+    }
+    /*
+     * ...otherwise, enable auto curve selection (OpenSSL 1.0.2 and later)
+     * or configure NIST P-256 (required to enable ECDHE for earlier versions)
+     */
+    else {
+#if defined(SSL_CTX_set_ecdh_auto)
+        SSL_CTX_set_ecdh_auto(mctx->ssl_ctx, 1);
+#else
+        SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx,
+                             EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+#endif
+    }
+#endif
+
+    return APR_SUCCESS;
+}
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+static apr_status_t ssl_init_ticket_key(server_rec *s,
+                                        apr_pool_t *p,
+                                        apr_pool_t *ptemp,
+                                        modssl_ctx_t *mctx)
+{
+    apr_status_t rv;
+    apr_file_t *fp;
+    apr_size_t len;
+    char buf[TLSEXT_TICKET_KEY_LEN];
+    char *path;
+    modssl_ticket_key_t *ticket_key = mctx->ticket_key;
+
+    if (!ticket_key->file_path) {
+        return APR_SUCCESS;
+    }
+
+    path = ap_server_root_relative(p, ticket_key->file_path);
+
+    rv = apr_file_open(&fp, path, APR_READ|APR_BINARY,
+                       APR_OS_DEFAULT, ptemp);
+
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02286)
+                     "Failed to open ticket key file %s: (%d) %pm",
+                     path, rv, &rv);
+        return ssl_die(s);
+    }
+
+    rv = apr_file_read_full(fp, &buf[0], TLSEXT_TICKET_KEY_LEN, &len);
+
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02287)
+                     "Failed to read %d bytes from %s: (%d) %pm",
+                     TLSEXT_TICKET_KEY_LEN, path, rv, &rv);
+        return ssl_die(s);
+    }
+
+    memcpy(ticket_key->key_name, buf, 16);
+    memcpy(ticket_key->hmac_secret, buf + 16, 16);
+    memcpy(ticket_key->aes_key, buf + 32, 16);
+
+    if (!SSL_CTX_set_tlsext_ticket_key_cb(mctx->ssl_ctx,
+                                          ssl_callback_SessionTicket)) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01913)
+                     "Unable to initialize TLS session ticket key callback "
+                     "(incompatible OpenSSL version?)");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02288)
+                 "TLS session ticket key for %s successfully loaded from %s",
+                 (mySrvConfig(s))->vhost_id, path);
+
+    return APR_SUCCESS;
+}
+#endif
+
+static apr_status_t ssl_init_proxy_certs(server_rec *s,
+                                         apr_pool_t *p,
+                                         apr_pool_t *ptemp,
+                                         modssl_ctx_t *mctx)
+{
+    int n, ncerts = 0;
+    STACK_OF(X509_INFO) *sk;
+    modssl_pk_proxy_t *pkp = mctx->pkp;
+    STACK_OF(X509) *chain;
+    X509_STORE_CTX *sctx;
+    X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+
+    SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
+                               ssl_callback_proxy_cert);
+
+    if (!(pkp->cert_file || pkp->cert_path)) {
+        return APR_SUCCESS;
+    }
+
+    sk = sk_X509_INFO_new_null();
+
+    if (pkp->cert_file) {
+        SSL_X509_INFO_load_file(ptemp, sk, pkp->cert_file);
+    }
+
+    if (pkp->cert_path) {
+        SSL_X509_INFO_load_path(ptemp, sk, pkp->cert_path);
+    }
+
+    if ((ncerts = sk_X509_INFO_num(sk)) <= 0) {
+        sk_X509_INFO_free(sk);
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02206)
+                     "no client certs found for SSL proxy");
+        return APR_SUCCESS;
+    }
+
+    /* Check that all client certs have got certificates and private
+     * keys. */
+    for (n = 0; n < ncerts; n++) {
+        X509_INFO *inf = sk_X509_INFO_value(sk, n);
+
+        if (!inf->x509 || !inf->x_pkey || !inf->x_pkey->dec_pkey ||
+            inf->enc_data) {
+            sk_X509_INFO_free(sk);
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, APLOGNO(02252)
+                         "incomplete client cert configured for SSL proxy "
+                         "(missing or encrypted private key?)");
+            return ssl_die(s);
+        }
+        
+        if (X509_check_private_key(inf->x509, inf->x_pkey->dec_pkey) != 1) {
+            ssl_log_xerror(SSLLOG_MARK, APLOG_STARTUP, 0, ptemp, s, inf->x509,
+                           APLOGNO(02326) "proxy client certificate and "
+                           "private key do not match");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+            return ssl_die(s);
+        }
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02207)
+                 "loaded %d client certs for SSL proxy",
+                 ncerts);
+    pkp->certs = sk;
+
+
+    if (!pkp->ca_cert_file || !store) {
+        return APR_SUCCESS;
+    }
+
+    /* If SSLProxyMachineCertificateChainFile is configured, load all
+     * the CA certs and have OpenSSL attempt to construct a full chain
+     * from each configured end-entity cert up to a root.  This will
+     * allow selection of the correct cert given a list of root CA
+     * names in the certificate request from the server.  */
+    pkp->ca_certs = (STACK_OF(X509) **) apr_pcalloc(p, ncerts * sizeof(sk));
+    sctx = X509_STORE_CTX_new();
+
+    if (!sctx) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02208)
+                     "SSL proxy client cert initialization failed");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    X509_STORE_load_locations(store, pkp->ca_cert_file, NULL);
+
+    for (n = 0; n < ncerts; n++) {
+        int i;
+
+        X509_INFO *inf = sk_X509_INFO_value(pkp->certs, n);
+        X509_STORE_CTX_init(sctx, store, inf->x509, NULL);
+
+        /* Attempt to verify the client cert */
+        if (X509_verify_cert(sctx) != 1) {
+            int err = X509_STORE_CTX_get_error(sctx);
+            ssl_log_xerror(SSLLOG_MARK, APLOG_WARNING, 0, ptemp, s, inf->x509,
+                           APLOGNO(02270) "SSL proxy client cert chain "
+                           "verification failed: %s :",
+                           X509_verify_cert_error_string(err));
+        }
+
+        /* Clear X509_verify_cert errors */
+        ERR_clear_error();
+
+        /* Obtain a copy of the verified chain */
+        chain = X509_STORE_CTX_get1_chain(sctx);
+
+        if (chain != NULL) {
+            /* Discard end entity cert from the chain */
+            X509_free(sk_X509_shift(chain));
+
+            if ((i = sk_X509_num(chain)) > 0) {
+                /* Store the chain for later use */
+                pkp->ca_certs[n] = chain;
+            }
+            else {
+                /* Discard empty chain */
+                sk_X509_pop_free(chain, X509_free);
+                pkp->ca_certs[n] = NULL;
+            }
+
+            ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, ptemp, s, inf->x509,
+                           APLOGNO(02271)
+                           "loaded %i intermediate CA%s for cert %i: ",
+                           i, i == 1 ? "" : "s", n);
+            if (i > 0) {
+                int j;
+                for (j = 0; j < i; j++) {
+                    ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, ptemp, s,
+                                   sk_X509_value(chain, j), "%i:", j);
+                }
+            }
+        }
+
+        /* get ready for next X509_STORE_CTX_init */
+        X509_STORE_CTX_cleanup(sctx);
+    }
+
+    X509_STORE_CTX_free(sctx);
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_proxy_ctx(server_rec *s,
+                                       apr_pool_t *p,
+                                       apr_pool_t *ptemp,
+                                       SSLSrvConfigRec *sc)
+{
+    apr_status_t rv;
+
+    if ((rv = ssl_init_ctx(s, p, ptemp, sc->proxy)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    if ((rv = ssl_init_proxy_certs(s, p, ptemp, sc->proxy)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_init_server_ctx(server_rec *s,
+                                        apr_pool_t *p,
+                                        apr_pool_t *ptemp,
+                                        SSLSrvConfigRec *sc,
+                                        apr_array_header_t *pphrases)
+{
+    apr_status_t rv;
+#ifdef HAVE_SSL_CONF_CMD
+    ssl_ctx_param_t *param = (ssl_ctx_param_t *)sc->server->ssl_ctx_param->elts;
+    SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config;
+    int i;
+#endif
+
+    /*
+     *  Check for problematic re-initializations
+     */
+    if (sc->server->ssl_ctx) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02569)
+                     "Illegal attempt to re-initialise SSL for server "
+                     "(SSLEngine On should go in the VirtualHost, not in global scope.)");
+        return APR_EGENERAL;
+    }
+
+    if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    if ((rv = ssl_init_server_certs(s, p, ptemp, sc->server, pphrases))
+        != APR_SUCCESS) {
+        return rv;
+    }
+
+#ifdef HAVE_SSL_CONF_CMD
+    SSL_CONF_CTX_set_ssl_ctx(cctx, sc->server->ssl_ctx);
+    for (i = 0; i < sc->server->ssl_ctx_param->nelts; i++, param++) {
+        ERR_clear_error();
+        if (SSL_CONF_cmd(cctx, param->name, param->value) <= 0) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02407)
+                         "\"SSLOpenSSLConfCmd %s %s\" failed for %s",
+                         param->name, param->value, sc->vhost_id);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+            return ssl_die(s);
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02556)
+                         "\"SSLOpenSSLConfCmd %s %s\" applied to %s",
+                         param->name, param->value, sc->vhost_id);
+        }
+    }
+
+    if (SSL_CONF_CTX_finish(cctx) == 0) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02547)
+                         "SSL_CONF_CTX_finish() failed");
+            SSL_CONF_CTX_free(cctx);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+            return ssl_die(s);
+    }
+    SSL_CONF_CTX_free(cctx);
+#endif
+
+    if (SSL_CTX_check_private_key(sc->server->ssl_ctx) != 1) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02572)
+                     "Failed to configure at least one certificate and key "
+                     "for %s", sc->vhost_id);
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+#if defined(HAVE_OCSP_STAPLING) && defined(SSL_CTRL_SET_CURRENT_CERT)
+    /*
+     * OpenSSL 1.0.2 and later allows iterating over all SSL_CTX certs
+     * by means of SSL_CTX_set_current_cert. Enabling stapling at this
+     * (late) point makes sure that we catch both certificates loaded
+     * via SSLCertificateFile and SSLOpenSSLConfCmd Certificate.
+     */
+    if (sc->server->stapling_enabled == TRUE) {
+        X509 *cert;
+        int i = 0;
+        int ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
+                                           SSL_CERT_SET_FIRST);
+        while (ret) {
+            cert = SSL_CTX_get0_certificate(sc->server->ssl_ctx);
+            if (!cert || !ssl_stapling_init_cert(s, sc->server, cert)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02604)
+                             "Unable to configure certificate %s:%d "
+                             "for stapling", sc->vhost_id, i);
+            }
+            ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx,
+                                           SSL_CERT_SET_NEXT);
+            i++;
+        }
+    }
+#endif
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+    if ((rv = ssl_init_ticket_key(s, p, ptemp, sc->server)) != APR_SUCCESS) {
+        return rv;
+    }
+#endif
+
+    SSL_CTX_set_timeout(sc->server->ssl_ctx,
+                        sc->session_cache_timeout == UNSET ?
+                        SSL_SESSION_CACHE_TIMEOUT : sc->session_cache_timeout);
+
+    return APR_SUCCESS;
+}
+
+/*
+ * Configure a particular server
+ */
+apr_status_t ssl_init_ConfigureServer(server_rec *s,
+                                      apr_pool_t *p,
+                                      apr_pool_t *ptemp,
+                                      SSLSrvConfigRec *sc,
+                                      apr_array_header_t *pphrases)
+{
+    apr_status_t rv;
+
+    /* Initialize the server if SSL is enabled or optional.
+     */
+    if ((sc->enabled == SSL_ENABLED_TRUE) || (sc->enabled == SSL_ENABLED_OPTIONAL)) {
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01914)
+                     "Configuring server %s for SSL protocol", sc->vhost_id);
+        if ((rv = ssl_init_server_ctx(s, p, ptemp, sc, pphrases))
+            != APR_SUCCESS) {
+            return rv;
+        }
+    }
+
+    if (sc->proxy_enabled) {
+        if ((rv = ssl_init_proxy_ctx(s, p, ptemp, sc)) != APR_SUCCESS) {
+            return rv;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t ssl_init_CheckServers(server_rec *base_server, apr_pool_t *p)
+{
+    server_rec *s;
+    SSLSrvConfigRec *sc;
+#ifndef HAVE_TLSEXT
+    server_rec *ps;
+    apr_hash_t *table;
+    const char *key;
+    apr_ssize_t klen;
+
+    BOOL conflict = FALSE;
+#endif
+
+    /*
+     * Give out warnings when a server has HTTPS configured
+     * for the HTTP port or vice versa
+     */
+    for (s = base_server; s; s = s->next) {
+        sc = mySrvConfig(s);
+
+        if ((sc->enabled == SSL_ENABLED_TRUE) && (s->port == DEFAULT_HTTP_PORT)) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+                         base_server, APLOGNO(01915)
+                         "Init: (%s) You configured HTTPS(%d) "
+                         "on the standard HTTP(%d) port!",
+                         ssl_util_vhostid(p, s),
+                         DEFAULT_HTTPS_PORT, DEFAULT_HTTP_PORT);
+        }
+
+        if ((sc->enabled == SSL_ENABLED_FALSE) && (s->port == DEFAULT_HTTPS_PORT)) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+                         base_server, APLOGNO(01916)
+                         "Init: (%s) You configured HTTP(%d) "
+                         "on the standard HTTPS(%d) port!",
+                         ssl_util_vhostid(p, s),
+                         DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);
+        }
+    }
+
+#ifndef HAVE_TLSEXT
+    /*
+     * Give out warnings when more than one SSL-aware virtual server uses the
+     * same IP:port and an OpenSSL version without support for TLS extensions
+     * (SNI in particular) is used.
+     */
+    table = apr_hash_make(p);
+
+    for (s = base_server; s; s = s->next) {
+        char *addr;
+
+        sc = mySrvConfig(s);
+
+        if (!((sc->enabled == SSL_ENABLED_TRUE) && s->addrs)) {
+            continue;
+        }
+
+        apr_sockaddr_ip_get(&addr, s->addrs->host_addr);
+        key = apr_psprintf(p, "%s:%u", addr, s->addrs->host_port);
+        klen = strlen(key);
+
+        if ((ps = (server_rec *)apr_hash_get(table, key, klen))) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server,
+                         "Init: SSL server IP/port conflict: "
+                         "%s (%s:%d) vs. %s (%s:%d)",
+                         ssl_util_vhostid(p, s),
+                         (s->defn_name ? s->defn_name : "unknown"),
+                         s->defn_line_number,
+                         ssl_util_vhostid(p, ps),
+                         (ps->defn_name ? ps->defn_name : "unknown"),
+                         ps->defn_line_number);
+            conflict = TRUE;
+            continue;
+        }
+
+        apr_hash_set(table, key, klen, s);
+    }
+
+    if (conflict) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, APLOGNO(01917)
+                     "Init: Name-based SSL virtual hosts require "
+                     "an OpenSSL version with support for TLS extensions "
+                     "(RFC 6066 - Server Name Indication / SNI), "
+                     "but the currently used library version (%s) is "
+                     "lacking this feature", SSLeay_version(SSLEAY_VERSION));
+    }
+#endif
+
+    return APR_SUCCESS;
+}
+
+static int ssl_init_FindCAList_X509NameCmp(const X509_NAME * const *a,
+                                           const X509_NAME * const *b)
+{
+    return(X509_NAME_cmp(*a, *b));
+}
+
+static void ssl_init_PushCAList(STACK_OF(X509_NAME) *ca_list,
+                                server_rec *s, apr_pool_t *ptemp,
+                                const char *file)
+{
+    int n;
+    STACK_OF(X509_NAME) *sk;
+
+    sk = (STACK_OF(X509_NAME) *)
+             SSL_load_client_CA_file(file);
+
+    if (!sk) {
+        return;
+    }
+
+    for (n = 0; n < sk_X509_NAME_num(sk); n++) {
+        X509_NAME *name = sk_X509_NAME_value(sk, n);
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02209)
+                     "CA certificate: %s",
+                     SSL_X509_NAME_to_string(ptemp, name, 0));
+
+        /*
+         * note that SSL_load_client_CA_file() checks for duplicates,
+         * but since we call it multiple times when reading a directory
+         * we must also check for duplicates ourselves.
+         */
+
+        if (sk_X509_NAME_find(ca_list, name) < 0) {
+            /* this will be freed when ca_list is */
+            sk_X509_NAME_push(ca_list, name);
+        }
+        else {
+            /* need to free this ourselves, else it will leak */
+            X509_NAME_free(name);
+        }
+    }
+
+    sk_X509_NAME_free(sk);
+}
+
+STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s,
+                                         apr_pool_t *ptemp,
+                                         const char *ca_file,
+                                         const char *ca_path)
+{
+    STACK_OF(X509_NAME) *ca_list;
+
+    /*
+     * Start with a empty stack/list where new
+     * entries get added in sorted order.
+     */
+    ca_list = sk_X509_NAME_new(ssl_init_FindCAList_X509NameCmp);
+
+    /*
+     * Process CA certificate bundle file
+     */
+    if (ca_file) {
+        ssl_init_PushCAList(ca_list, s, ptemp, ca_file);
+        /*
+         * If ca_list is still empty after trying to load ca_file
+         * then the file failed to load, and users should hear about that.
+         */
+        if (sk_X509_NAME_num(ca_list) == 0) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02210)
+                    "Failed to load SSLCACertificateFile: %s", ca_file);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+        }
+    }
+
+    /*
+     * Process CA certificate path files
+     */
+    if (ca_path) {
+        apr_dir_t *dir;
+        apr_finfo_t direntry;
+        apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
+        apr_status_t rv;
+
+        if ((rv = apr_dir_open(&dir, ca_path, ptemp)) != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02211)
+                    "Failed to open Certificate Path `%s'",
+                    ca_path);
+            sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
+            return NULL;
+        }
+
+        while ((apr_dir_read(&direntry, finfo_flags, dir)) == APR_SUCCESS) {
+            const char *file;
+            if (direntry.filetype == APR_DIR) {
+                continue; /* don't try to load directories */
+            }
+            file = apr_pstrcat(ptemp, ca_path, "/", direntry.name, NULL);
+            ssl_init_PushCAList(ca_list, s, ptemp, file);
+        }
+
+        apr_dir_close(dir);
+    }
+
+    /*
+     * Cleanup
+     */
+    (void) sk_X509_NAME_set_cmp_func(ca_list, NULL);
+
+    return ca_list;
+}
+
+void ssl_init_Child(apr_pool_t *p, server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    mc->pid = getpid(); /* only call getpid() once per-process */
+
+    /* XXX: there should be an ap_srand() function */
+    srand((unsigned int)time(NULL));
+
+    /* open the mutex lockfile */
+    ssl_mutex_reinit(s, p);
+#ifdef HAVE_OCSP_STAPLING
+    ssl_stapling_mutex_reinit(s, p);
+#endif
+}
+
+#define MODSSL_CFG_ITEM_FREE(func, item) \
+    if (item) { \
+        func(item); \
+        item = NULL; \
+    }
+
+static void ssl_init_ctx_cleanup(modssl_ctx_t *mctx)
+{
+    MODSSL_CFG_ITEM_FREE(SSL_CTX_free, mctx->ssl_ctx);
+
+#ifdef HAVE_SRP
+    if (mctx->srp_vbase != NULL) {
+        SRP_VBASE_free(mctx->srp_vbase);
+        mctx->srp_vbase = NULL;
+    }
+#endif
+}
+
+static void ssl_init_ctx_cleanup_proxy(modssl_ctx_t *mctx)
+{
+    ssl_init_ctx_cleanup(mctx);
+
+    if (mctx->pkp->certs) {
+        int i = 0;
+        int ncerts = sk_X509_INFO_num(mctx->pkp->certs);
+
+        if (mctx->pkp->ca_certs) {
+            for (i = 0; i < ncerts; i++) {
+                if (mctx->pkp->ca_certs[i] != NULL) {
+                    sk_X509_pop_free(mctx->pkp->ca_certs[i], X509_free);
+                }
+            }
+        }
+
+        sk_X509_INFO_pop_free(mctx->pkp->certs, X509_INFO_free);
+        mctx->pkp->certs = NULL;
+    }
+}
+
+apr_status_t ssl_init_ModuleKill(void *data)
+{
+    SSLSrvConfigRec *sc;
+    server_rec *base_server = (server_rec *)data;
+    server_rec *s;
+
+    /*
+     * Drop the session cache and mutex
+     */
+    ssl_scache_kill(base_server);
+
+    /*
+     * Free the non-pool allocated structures
+     * in the per-server configurations
+     */
+    for (s = base_server; s; s = s->next) {
+        sc = mySrvConfig(s);
+
+        ssl_init_ctx_cleanup_proxy(sc->proxy);
+
+        ssl_init_ctx_cleanup(sc->server);
+    }
+
+    free_dh_params();
+
+    return APR_SUCCESS;
+}
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_io.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_io.c
new file mode 100644 (file)
index 0000000..168f43f
--- /dev/null
@@ -0,0 +1,2098 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_io.c
+ *  I/O Functions
+ */
+                             /* ``MY HACK: This universe.
+                                  Just one little problem:
+                                  core keeps dumping.''
+                                            -- Unknown    */
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "apr_date.h"
+
+/*  _________________________________________________________________
+**
+**  I/O Hooks
+**  _________________________________________________________________
+*/
+
+/* This file is designed to be the bridge between OpenSSL and httpd.
+ * However, we really don't expect anyone (let alone ourselves) to
+ * remember what is in this file.  So, first, a quick overview.
+ *
+ * In this file, you will find:
+ * - ssl_io_filter_input    (Apache input filter)
+ * - ssl_io_filter_output   (Apache output filter)
+ *
+ * - bio_filter_in_*        (OpenSSL input filter)
+ * - bio_filter_out_*       (OpenSSL output filter)
+ *
+ * The input chain is roughly:
+ *
+ * ssl_io_filter_input->ssl_io_input_read->SSL_read->...
+ * ...->bio_filter_in_read->ap_get_brigade/next-httpd-filter
+ *
+ * In mortal terminology, we do the following:
+ * - Receive a request for data to the SSL input filter
+ * - Call a helper function once we know we should perform a read
+ * - Call OpenSSL's SSL_read()
+ * - SSL_read() will then call bio_filter_in_read
+ * - bio_filter_in_read will then try to fetch data from the next httpd filter
+ * - bio_filter_in_read will flatten that data and return it to SSL_read
+ * - SSL_read will then decrypt the data
+ * - ssl_io_input_read will then receive decrypted data as a char* and
+ *   ensure that there were no read errors
+ * - The char* is placed in a brigade and returned
+ *
+ * Since connection-level input filters in httpd need to be able to
+ * handle AP_MODE_GETLINE calls (namely identifying LF-terminated strings),
+ * ssl_io_input_getline which will handle this special case.
+ *
+ * Due to AP_MODE_GETLINE and AP_MODE_SPECULATIVE, we may sometimes have
+ * 'leftover' decoded data which must be setaside for the next read.  That
+ * is currently handled by the char_buffer_{read|write} functions.  So,
+ * ssl_io_input_read may be able to fulfill reads without invoking
+ * SSL_read().
+ *
+ * Note that the filter context of ssl_io_filter_input and bio_filter_in_*
+ * are shared as bio_filter_in_ctx_t.
+ *
+ * Note that the filter is by choice limited to reading at most
+ * AP_IOBUFSIZE (8192 bytes) per call.
+ *
+ */
+
+/* this custom BIO allows us to hook SSL_write directly into
+ * an apr_bucket_brigade and use transient buckets with the SSL
+ * malloc-ed buffer, rather than copying into a mem BIO.
+ * also allows us to pass the brigade as data is being written
+ * rather than buffering up the entire response in the mem BIO.
+ *
+ * when SSL needs to flush (e.g. SSL_accept()), it will call BIO_flush()
+ * which will trigger a call to bio_filter_out_ctrl() -> bio_filter_out_flush().
+ * so we only need to flush the output ourselves if we receive an
+ * EOS or FLUSH bucket. this was not possible with the mem BIO where we
+ * had to flush all over the place not really knowing when it was required
+ * to do so.
+ */
+
+typedef struct {
+    SSL                *pssl;
+    BIO                *pbioRead;
+    BIO                *pbioWrite;
+    ap_filter_t        *pInputFilter;
+    ap_filter_t        *pOutputFilter;
+    SSLConnRec         *config;
+} ssl_filter_ctx_t;
+
+typedef struct {
+    ssl_filter_ctx_t *filter_ctx;
+    conn_rec *c;
+    apr_bucket_brigade *bb;    /* Brigade used as a buffer. */
+    apr_status_t rc;
+} bio_filter_out_ctx_t;
+
+static bio_filter_out_ctx_t *bio_filter_out_ctx_new(ssl_filter_ctx_t *filter_ctx,
+                                                    conn_rec *c)
+{
+    bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx));
+
+    outctx->filter_ctx = filter_ctx;
+    outctx->c = c;
+    outctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);
+
+    return outctx;
+}
+
+/* Pass an output brigade down the filter stack; returns 1 on success
+ * or -1 on failure. */
+static int bio_filter_out_pass(bio_filter_out_ctx_t *outctx)
+{
+    AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(outctx->bb));
+
+    outctx->rc = ap_pass_brigade(outctx->filter_ctx->pOutputFilter->next,
+                                 outctx->bb);
+    /* Fail if the connection was reset: */
+    if (outctx->rc == APR_SUCCESS && outctx->c->aborted) {
+        outctx->rc = APR_ECONNRESET;
+    }
+    return (outctx->rc == APR_SUCCESS) ? 1 : -1;
+}
+
+/* Send a FLUSH bucket down the output filter stack; returns 1 on
+ * success, -1 on failure. */
+static int bio_filter_out_flush(BIO *bio)
+{
+    bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr);
+    apr_bucket *e;
+
+    AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(outctx->bb));
+
+    e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
+
+    return bio_filter_out_pass(outctx);
+}
+
+static int bio_filter_create(BIO *bio)
+{
+    bio->shutdown = 1;
+    bio->init = 1;
+    bio->num = -1;
+    bio->ptr = NULL;
+
+    return 1;
+}
+
+static int bio_filter_destroy(BIO *bio)
+{
+    if (bio == NULL) {
+        return 0;
+    }
+
+    /* nothing to free here.
+     * apache will destroy the bucket brigade for us
+     */
+    return 1;
+}
+
+static int bio_filter_out_read(BIO *bio, char *out, int outl)
+{
+    /* this is never called */
+    return -1;
+}
+
+static int bio_filter_out_write(BIO *bio, const char *in, int inl)
+{
+    bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr);
+    apr_bucket *e;
+
+    /* Abort early if the client has initiated a renegotiation. */
+    if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
+        outctx->rc = APR_ECONNABORTED;
+        return -1;
+    }
+
+    /* when handshaking we'll have a small number of bytes.
+     * max size SSL will pass us here is about 16k.
+     * (16413 bytes to be exact)
+     */
+    BIO_clear_retry_flags(bio);
+
+    /* Use a transient bucket for the output data - any downstream
+     * filter must setaside if necessary. */
+    e = apr_bucket_transient_create(in, inl, outctx->bb->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
+
+    if (bio_filter_out_pass(outctx) < 0) {
+        return -1;
+    }
+
+    return inl;
+}
+
+static long bio_filter_out_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+    long ret = 1;
+    bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr);
+
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+    case BIO_CTRL_EOF:
+    case BIO_C_SET_BUF_MEM_EOF_RETURN:
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, outctx->c,
+                      "output bio: unhandled control %d", cmd);
+        ret = 0;
+        break;
+    case BIO_CTRL_WPENDING:
+    case BIO_CTRL_PENDING:
+    case BIO_CTRL_INFO:
+        ret = 0;
+        break;
+    case BIO_CTRL_GET_CLOSE:
+        ret = (long)bio->shutdown;
+        break;
+      case BIO_CTRL_SET_CLOSE:
+        bio->shutdown = (int)num;
+        break;
+      case BIO_CTRL_FLUSH:
+        ret = bio_filter_out_flush(bio);
+        break;
+      case BIO_CTRL_DUP:
+        ret = 1;
+        break;
+        /* N/A */
+      case BIO_C_SET_BUF_MEM:
+      case BIO_C_GET_BUF_MEM_PTR:
+        /* we don't care */
+      case BIO_CTRL_PUSH:
+      case BIO_CTRL_POP:
+      default:
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static int bio_filter_out_gets(BIO *bio, char *buf, int size)
+{
+    /* this is never called */
+    return -1;
+}
+
+static int bio_filter_out_puts(BIO *bio, const char *str)
+{
+    /* this is never called */
+    return -1;
+}
+
+static BIO_METHOD bio_filter_out_method = {
+    BIO_TYPE_MEM,
+    "APR output filter",
+    bio_filter_out_write,
+    bio_filter_out_read,     /* read is never called */
+    bio_filter_out_puts,     /* puts is never called */
+    bio_filter_out_gets,     /* gets is never called */
+    bio_filter_out_ctrl,
+    bio_filter_create,
+    bio_filter_destroy,
+    NULL
+};
+
+typedef struct {
+    int length;
+    char *value;
+} char_buffer_t;
+
+typedef struct {
+    SSL *ssl;
+    BIO *bio_out;
+    ap_filter_t *f;
+    apr_status_t rc;
+    ap_input_mode_t mode;
+    apr_read_type_e block;
+    apr_bucket_brigade *bb;
+    char_buffer_t cbuf;
+    apr_pool_t *pool;
+    char buffer[AP_IOBUFSIZE];
+    ssl_filter_ctx_t *filter_ctx;
+    int alpn_finished;  /* 1 if ALPN has finished, 0 otherwise */
+} bio_filter_in_ctx_t;
+
+/*
+ * this char_buffer api might seem silly, but we don't need to copy
+ * any of this data and we need to remember the length.
+ */
+
+/* Copy up to INL bytes from the char_buffer BUFFER into IN.  Note
+ * that due to the strange way this API is designed/used, the
+ * char_buffer object is used to cache a segment of inctx->buffer, and
+ * then this function called to copy (part of) that segment to the
+ * beginning of inctx->buffer.  So the segments to copy cannot be
+ * presumed to be non-overlapping, and memmove must be used. */
+static int char_buffer_read(char_buffer_t *buffer, char *in, int inl)
+{
+    if (!buffer->length) {
+        return 0;
+    }
+
+    if (buffer->length > inl) {
+        /* we have have enough to fill the caller's buffer */
+        memmove(in, buffer->value, inl);
+        buffer->value += inl;
+        buffer->length -= inl;
+    }
+    else {
+        /* swallow remainder of the buffer */
+        memmove(in, buffer->value, buffer->length);
+        inl = buffer->length;
+        buffer->value = NULL;
+        buffer->length = 0;
+    }
+
+    return inl;
+}
+
+static int char_buffer_write(char_buffer_t *buffer, char *in, int inl)
+{
+    buffer->value = in;
+    buffer->length = inl;
+    return inl;
+}
+
+/* This function will read from a brigade and discard the read buckets as it
+ * proceeds.  It will read at most *len bytes.
+ */
+static apr_status_t brigade_consume(apr_bucket_brigade *bb,
+                                    apr_read_type_e block,
+                                    char *c, apr_size_t *len)
+{
+    apr_size_t actual = 0;
+    apr_status_t status = APR_SUCCESS;
+
+    while (!APR_BRIGADE_EMPTY(bb)) {
+        apr_bucket *b = APR_BRIGADE_FIRST(bb);
+        const char *str;
+        apr_size_t str_len;
+        apr_size_t consume;
+
+        /* Justin points out this is an http-ism that might
+         * not fit if brigade_consume is added to APR.  Perhaps
+         * apr_bucket_read(eos_bucket) should return APR_EOF?
+         * Then this becomes mainline instead of a one-off.
+         */
+        if (APR_BUCKET_IS_EOS(b)) {
+            status = APR_EOF;
+            break;
+        }
+
+        /* The reason I'm not offering brigade_consume yet
+         * across to apr-util is that the following call
+         * illustrates how borked that API really is.  For
+         * this sort of case (caller provided buffer) it
+         * would be much more trivial for apr_bucket_consume
+         * to do all the work that follows, based on the
+         * particular characteristics of the bucket we are
+         * consuming here.
+         */
+        status = apr_bucket_read(b, &str, &str_len, block);
+
+        if (status != APR_SUCCESS) {
+            if (APR_STATUS_IS_EOF(status)) {
+                /* This stream bucket was consumed */
+                apr_bucket_delete(b);
+                continue;
+            }
+            break;
+        }
+
+        if (str_len > 0) {
+            /* Do not block once some data has been consumed */
+            block = APR_NONBLOCK_READ;
+
+            /* Assure we don't overflow. */
+            consume = (str_len + actual > *len) ? *len - actual : str_len;
+
+            memcpy(c, str, consume);
+
+            c += consume;
+            actual += consume;
+
+            if (consume >= b->length) {
+                /* This physical bucket was consumed */
+                apr_bucket_delete(b);
+            }
+            else {
+                /* Only part of this physical bucket was consumed */
+                b->start += consume;
+                b->length -= consume;
+            }
+        }
+        else if (b->length == 0) {
+            apr_bucket_delete(b);
+        }
+
+        /* This could probably be actual == *len, but be safe from stray
+         * photons. */
+        if (actual >= *len) {
+            break;
+        }
+    }
+
+    *len = actual;
+    return status;
+}
+
+/*
+ * this is the function called by SSL_read()
+ */
+static int bio_filter_in_read(BIO *bio, char *in, int inlen)
+{
+    apr_size_t inl = inlen;
+    bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)(bio->ptr);
+    apr_read_type_e block = inctx->block;
+
+    inctx->rc = APR_SUCCESS;
+
+    /* OpenSSL catches this case, so should we. */
+    if (!in)
+        return 0;
+
+    /* Abort early if the client has initiated a renegotiation. */
+    if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
+        inctx->rc = APR_ECONNABORTED;
+        return -1;
+    }
+
+    /* In theory, OpenSSL should flush as necessary, but it is known
+     * not to do so correctly in some cases; see PR 46952.
+     *
+     * Historically, this flush call was performed only for an SSLv2
+     * connection or for a proxy connection.  Calling _out_flush
+     * should be very cheap in cases where it is unnecessary (and no
+     * output is buffered) so the performance impact of doing it
+     * unconditionally should be minimal.
+     */
+    if (bio_filter_out_flush(inctx->bio_out) < 0) {
+        bio_filter_out_ctx_t *outctx = inctx->bio_out->ptr;
+        inctx->rc = outctx->rc;
+        return -1;
+    }
+
+    BIO_clear_retry_flags(bio);
+
+    if (!inctx->bb) {
+        inctx->rc = APR_EOF;
+        return -1;
+    }
+
+    if (APR_BRIGADE_EMPTY(inctx->bb)) {
+
+        inctx->rc = ap_get_brigade(inctx->f->next, inctx->bb,
+                                   AP_MODE_READBYTES, block,
+                                   inl);
+
+        /* If the read returns EAGAIN or success with an empty
+         * brigade, return an error after setting the retry flag;
+         * SSL_read() will then return -1, and SSL_get_error() will
+         * indicate SSL_ERROR_WANT_READ. */
+        if (APR_STATUS_IS_EAGAIN(inctx->rc) || APR_STATUS_IS_EINTR(inctx->rc)
+               || (inctx->rc == APR_SUCCESS && APR_BRIGADE_EMPTY(inctx->bb))) {
+            BIO_set_retry_read(bio);
+            return -1;
+        }
+
+        if (inctx->rc != APR_SUCCESS) {
+            /* Unexpected errors discard the brigade */
+            apr_brigade_cleanup(inctx->bb);
+            inctx->bb = NULL;
+            return -1;
+        }
+    }
+
+    inctx->rc = brigade_consume(inctx->bb, block, in, &inl);
+
+    if (inctx->rc == APR_SUCCESS) {
+        return (int)inl;
+    }
+
+    if (APR_STATUS_IS_EAGAIN(inctx->rc)
+            || APR_STATUS_IS_EINTR(inctx->rc)) {
+        BIO_set_retry_read(bio);
+        return (int)inl;
+    }
+
+    /* Unexpected errors and APR_EOF clean out the brigade.
+     * Subsequent calls will return APR_EOF.
+     */
+    apr_brigade_cleanup(inctx->bb);
+    inctx->bb = NULL;
+
+    if (APR_STATUS_IS_EOF(inctx->rc) && inl) {
+        /* Provide the results of this read pass,
+         * without resetting the BIO retry_read flag
+         */
+        return (int)inl;
+    }
+
+    return -1;
+}
+
+
+static BIO_METHOD bio_filter_in_method = {
+    BIO_TYPE_MEM,
+    "APR input filter",
+    NULL,                       /* write is never called */
+    bio_filter_in_read,
+    NULL,                       /* puts is never called */
+    NULL,                       /* gets is never called */
+    NULL,                       /* ctrl is never called */
+    bio_filter_create,
+    bio_filter_destroy,
+    NULL
+};
+
+
+static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
+                                      char *buf,
+                                      apr_size_t *len)
+{
+    apr_size_t wanted = *len;
+    apr_size_t bytes = 0;
+    int rc;
+
+    *len = 0;
+
+    /* If we have something leftover from last time, try that first. */
+    if ((bytes = char_buffer_read(&inctx->cbuf, buf, wanted))) {
+        *len = bytes;
+        if (inctx->mode == AP_MODE_SPECULATIVE) {
+            /* We want to rollback this read. */
+            if (inctx->cbuf.length > 0) {
+                inctx->cbuf.value -= bytes;
+                inctx->cbuf.length += bytes;
+            } else {
+                char_buffer_write(&inctx->cbuf, buf, (int)bytes);
+            }
+            return APR_SUCCESS;
+        }
+        /* This could probably be *len == wanted, but be safe from stray
+         * photons.
+         */
+        if (*len >= wanted) {
+            return APR_SUCCESS;
+        }
+        if (inctx->mode == AP_MODE_GETLINE) {
+            if (memchr(buf, APR_ASCII_LF, *len)) {
+                return APR_SUCCESS;
+            }
+        }
+        else {
+            /* Down to a nonblock pattern as we have some data already
+             */
+            inctx->block = APR_NONBLOCK_READ;
+        }
+    }
+
+    while (1) {
+
+        if (!inctx->filter_ctx->pssl) {
+            /* Ensure a non-zero error code is returned */
+            if (inctx->rc == APR_SUCCESS) {
+                inctx->rc = APR_EGENERAL;
+            }
+            break;
+        }
+
+        /* SSL_read may not read because we haven't taken enough data
+         * from the stack.  This is where we want to consider all of
+         * the blocking and SPECULATIVE semantics
+         */
+        rc = SSL_read(inctx->filter_ctx->pssl, buf + bytes, wanted - bytes);
+
+        if (rc > 0) {
+            *len += rc;
+            if (inctx->mode == AP_MODE_SPECULATIVE) {
+                /* We want to rollback this read. */
+                char_buffer_write(&inctx->cbuf, buf, rc);
+            }
+            return inctx->rc;
+        }
+        else if (rc == 0) {
+            /* If EAGAIN, we will loop given a blocking read,
+             * otherwise consider ourselves at EOF.
+             */
+            if (APR_STATUS_IS_EAGAIN(inctx->rc)
+                    || APR_STATUS_IS_EINTR(inctx->rc)) {
+                /* Already read something, return APR_SUCCESS instead.
+                 * On win32 in particular, but perhaps on other kernels,
+                 * a blocking call isn't 'always' blocking.
+                 */
+                if (*len > 0) {
+                    inctx->rc = APR_SUCCESS;
+                    break;
+                }
+                if (inctx->block == APR_NONBLOCK_READ) {
+                    break;
+                }
+            }
+            else {
+                if (*len > 0) {
+                    inctx->rc = APR_SUCCESS;
+                }
+                else {
+                    inctx->rc = APR_EOF;
+                }
+                break;
+            }
+        }
+        else /* (rc < 0) */ {
+            int ssl_err = SSL_get_error(inctx->filter_ctx->pssl, rc);
+            conn_rec *c = (conn_rec*)SSL_get_app_data(inctx->filter_ctx->pssl);
+
+            if (ssl_err == SSL_ERROR_WANT_READ) {
+                /*
+                 * If OpenSSL wants to read more, and we were nonblocking,
+                 * report as an EAGAIN.  Otherwise loop, pulling more
+                 * data from network filter.
+                 *
+                 * (This is usually the case when the client forces an SSL
+                 * renegotiation which is handled implicitly by OpenSSL.)
+                 */
+                inctx->rc = APR_EAGAIN;
+
+                if (*len > 0) {
+                    inctx->rc = APR_SUCCESS;
+                    break;
+                }
+                if (inctx->block == APR_NONBLOCK_READ) {
+                    break;
+                }
+                continue;  /* Blocking and nothing yet?  Try again. */
+            }
+            else if (ssl_err == SSL_ERROR_SYSCALL) {
+                if (APR_STATUS_IS_EAGAIN(inctx->rc)
+                        || APR_STATUS_IS_EINTR(inctx->rc)) {
+                    /* Already read something, return APR_SUCCESS instead. */
+                    if (*len > 0) {
+                        inctx->rc = APR_SUCCESS;
+                        break;
+                    }
+                    if (inctx->block == APR_NONBLOCK_READ) {
+                        break;
+                    }
+                    continue;  /* Blocking and nothing yet?  Try again. */
+                }
+                else {
+                    ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c, APLOGNO(01991)
+                                  "SSL input filter read failed.");
+                }
+            }
+            else /* if (ssl_err == SSL_ERROR_SSL) */ {
+                /*
+                 * Log SSL errors and any unexpected conditions.
+                 */
+                ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c, APLOGNO(01992)
+                              "SSL library error %d reading data", ssl_err);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, mySrvFromConn(c));
+
+            }
+            if (inctx->rc == APR_SUCCESS) {
+                inctx->rc = APR_EGENERAL;
+            }
+            break;
+        }
+    }
+    return inctx->rc;
+}
+
+/* Read a line of input from the SSL input layer into buffer BUF of
+ * length *LEN; updating *len to reflect the length of the line
+ * including the LF character. */
+static apr_status_t ssl_io_input_getline(bio_filter_in_ctx_t *inctx,
+                                         char *buf,
+                                         apr_size_t *len)
+{
+    const char *pos = NULL;
+    apr_status_t status;
+    apr_size_t tmplen = *len, buflen = *len, offset = 0;
+
+    *len = 0;
+
+    /*
+     * in most cases we get all the headers on the first SSL_read.
+     * however, in certain cases SSL_read will only get a partial
+     * chunk of the headers, so we try to read until LF is seen.
+     */
+
+    while (tmplen > 0) {
+        status = ssl_io_input_read(inctx, buf + offset, &tmplen);
+
+        if (status != APR_SUCCESS) {
+            if (APR_STATUS_IS_EAGAIN(status) && (*len > 0)) {
+                /* Save the part of the line we already got */
+                char_buffer_write(&inctx->cbuf, buf, *len);
+            }
+            return status;
+        }
+
+        *len += tmplen;
+
+        if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
+            break;
+        }
+
+        offset += tmplen;
+        tmplen = buflen - offset;
+    }
+
+    if (pos) {
+        char *value;
+        int length;
+        apr_size_t bytes = pos - buf;
+
+        bytes += 1;
+        value = buf + bytes;
+        length = *len - bytes;
+
+        char_buffer_write(&inctx->cbuf, value, length);
+
+        *len = bytes;
+    }
+
+    return APR_SUCCESS;
+}
+
+
+static apr_status_t ssl_filter_write(ap_filter_t *f,
+                                     const char *data,
+                                     apr_size_t len)
+{
+    ssl_filter_ctx_t *filter_ctx = f->ctx;
+    bio_filter_out_ctx_t *outctx;
+    int res;
+
+    /* write SSL */
+    if (filter_ctx->pssl == NULL) {
+        return APR_EGENERAL;
+    }
+
+    outctx = (bio_filter_out_ctx_t *)filter_ctx->pbioWrite->ptr;
+    res = SSL_write(filter_ctx->pssl, (unsigned char *)data, len);
+
+    if (res < 0) {
+        int ssl_err = SSL_get_error(filter_ctx->pssl, res);
+        conn_rec *c = (conn_rec*)SSL_get_app_data(outctx->filter_ctx->pssl);
+
+        if (ssl_err == SSL_ERROR_WANT_WRITE) {
+            /*
+             * If OpenSSL wants to write more, and we were nonblocking,
+             * report as an EAGAIN.  Otherwise loop, pushing more
+             * data at the network filter.
+             *
+             * (This is usually the case when the client forces an SSL
+             * renegotiation which is handled implicitly by OpenSSL.)
+             */
+            outctx->rc = APR_EAGAIN;
+        }
+        else if (ssl_err == SSL_ERROR_WANT_READ) {
+            /*
+             * If OpenSSL wants to read during write, and we were
+             * nonblocking, set the sense explicitly to read and
+             * report as an EAGAIN.
+             *
+             * (This is usually the case when the client forces an SSL
+             * renegotiation which is handled implicitly by OpenSSL.)
+             */
+            outctx->c->cs->sense = CONN_SENSE_WANT_READ;
+            outctx->rc = APR_EAGAIN;
+        }
+        else if (ssl_err == SSL_ERROR_SYSCALL) {
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01993)
+                          "SSL output filter write failed.");
+        }
+        else /* if (ssl_err == SSL_ERROR_SSL) */ {
+            /*
+             * Log SSL errors
+             */
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01994)
+                          "SSL library error %d writing data", ssl_err);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, mySrvFromConn(c));
+        }
+        if (outctx->rc == APR_SUCCESS) {
+            outctx->rc = APR_EGENERAL;
+        }
+    }
+    else if ((apr_size_t)res != len) {
+        conn_rec *c = f->c;
+        char *reason = "reason unknown";
+
+        /* XXX: probably a better way to determine this */
+        if (SSL_total_renegotiations(filter_ctx->pssl)) {
+            reason = "likely due to failed renegotiation";
+        }
+
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01995)
+                      "failed to write %" APR_SSIZE_T_FMT
+                      " of %" APR_SIZE_T_FMT " bytes (%s)",
+                      len - (apr_size_t)res, len, reason);
+
+        outctx->rc = APR_EGENERAL;
+    }
+    return outctx->rc;
+}
+
+/* Just use a simple request.  Any request will work for this, because
+ * we use a flag in the conn_rec->conn_vector now.  The fake request just
+ * gets the request back to the Apache core so that a response can be sent.
+ * Since we use an HTTP/1.x request, we also have to inject the empty line
+ * that terminates the headers, or the core will read more data from the
+ * socket.
+ */
+#define HTTP_ON_HTTPS_PORT \
+    "GET / HTTP/1.0" CRLF
+
+#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
+    apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT, \
+                               sizeof(HTTP_ON_HTTPS_PORT) - 1, \
+                               alloc)
+
+/* Custom apr_status_t error code, used when a plain HTTP request is
+ * recevied on an SSL port. */
+#define MODSSL_ERROR_HTTP_ON_HTTPS (APR_OS_START_USERERR + 0)
+
+/* Custom apr_status_t error code, used when the proxy cannot
+ * establish an outgoing SSL connection. */
+#define MODSSL_ERROR_BAD_GATEWAY (APR_OS_START_USERERR + 1)
+
+static void ssl_io_filter_disable(SSLConnRec *sslconn, ap_filter_t *f)
+{
+    bio_filter_in_ctx_t *inctx = f->ctx;
+    SSL_free(inctx->ssl);
+    sslconn->ssl = NULL;
+    inctx->ssl = NULL;
+    inctx->filter_ctx->pssl = NULL;
+}
+
+static apr_status_t ssl_io_filter_error(ap_filter_t *f,
+                                        apr_bucket_brigade *bb,
+                                        apr_status_t status)
+{
+    SSLConnRec *sslconn = myConnConfig(f->c);
+    apr_bucket *bucket;
+    int send_eos = 1;
+
+    switch (status) {
+    case MODSSL_ERROR_HTTP_ON_HTTPS:
+            /* log the situation */
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01996)
+                         "SSL handshake failed: HTTP spoken on HTTPS port; "
+                         "trying to send HTML error page");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, sslconn->server);
+
+            sslconn->non_ssl_request = NON_SSL_SEND_HDR_SEP;
+            ssl_io_filter_disable(sslconn, f);
+
+            /* fake the request line */
+            bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
+            send_eos = 0;
+            break;
+
+    case MODSSL_ERROR_BAD_GATEWAY:
+        bucket = ap_bucket_error_create(HTTP_BAD_REQUEST, NULL,
+                                        f->c->pool,
+                                        f->c->bucket_alloc);
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, APLOGNO(01997)
+                      "SSL handshake failed: sending 502");
+        break;
+
+    default:
+        return status;
+    }
+
+    APR_BRIGADE_INSERT_TAIL(bb, bucket);
+    if (send_eos) {
+        bucket = apr_bucket_eos_create(f->c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(bb, bucket);
+    }
+    return APR_SUCCESS;
+}
+
+static const char ssl_io_filter[] = "SSL/TLS Filter";
+static const char ssl_io_buffer[] = "SSL/TLS Buffer";
+static const char ssl_io_coalesce[] = "SSL/TLS Coalescing Filter";
+
+/*
+ *  Close the SSL part of the socket connection
+ *  (called immediately _before_ the socket is closed)
+ *  or called with
+ */
+static void ssl_filter_io_shutdown(ssl_filter_ctx_t *filter_ctx,
+                                   conn_rec *c, int abortive)
+{
+    SSL *ssl = filter_ctx->pssl;
+    const char *type = "";
+    SSLConnRec *sslconn = myConnConfig(c);
+    int shutdown_type;
+    int loglevel = APLOG_DEBUG;
+    const char *logno;
+
+    if (!ssl) {
+        return;
+    }
+
+    /*
+     * Now close the SSL layer of the connection. We've to take
+     * the TLSv1 standard into account here:
+     *
+     * | 7.2.1. Closure alerts
+     * |
+     * | The client and the server must share knowledge that the connection is
+     * | ending in order to avoid a truncation attack. Either party may
+     * | initiate the exchange of closing messages.
+     * |
+     * | close_notify
+     * |     This message notifies the recipient that the sender will not send
+     * |     any more messages on this connection. The session becomes
+     * |     unresumable if any connection is terminated without proper
+     * |     close_notify messages with level equal to warning.
+     * |
+     * | Either party may initiate a close by sending a close_notify alert.
+     * | Any data received after a closure alert is ignored.
+     * |
+     * | Each party is required to send a close_notify alert before closing
+     * | the write side of the connection. It is required that the other party
+     * | respond with a close_notify alert of its own and close down the
+     * | connection immediately, discarding any pending writes. It is not
+     * | required for the initiator of the close to wait for the responding
+     * | close_notify alert before closing the read side of the connection.
+     *
+     * This means we've to send a close notify message, but haven't to wait
+     * for the close notify of the client. Actually we cannot wait for the
+     * close notify of the client because some clients (including Netscape
+     * 4.x) don't send one, so we would hang.
+     */
+
+    /*
+     * exchange close notify messages, but allow the user
+     * to force the type of handshake via SetEnvIf directive
+     */
+    if (abortive) {
+        shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
+        type = "abortive";
+        logno = APLOGNO(01998);
+        loglevel = APLOG_INFO;
+    }
+    else switch (sslconn->shutdown_type) {
+      case SSL_SHUTDOWN_TYPE_UNCLEAN:
+        /* perform no close notify handshake at all
+           (violates the SSL/TLS standard!) */
+        shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
+        type = "unclean";
+        logno = APLOGNO(01999);
+        break;
+      case SSL_SHUTDOWN_TYPE_ACCURATE:
+        /* send close notify and wait for clients close notify
+           (standard compliant, but usually causes connection hangs) */
+        shutdown_type = 0;
+        type = "accurate";
+        logno = APLOGNO(02000);
+        break;
+      default:
+        /*
+         * case SSL_SHUTDOWN_TYPE_UNSET:
+         * case SSL_SHUTDOWN_TYPE_STANDARD:
+         */
+        /* send close notify, but don't wait for clients close notify
+           (standard compliant and safe, so it's the DEFAULT!) */
+        shutdown_type = SSL_RECEIVED_SHUTDOWN;
+        type = "standard";
+        logno = APLOGNO(02001);
+        break;
+    }
+
+    SSL_set_shutdown(ssl, shutdown_type);
+    SSL_smart_shutdown(ssl);
+
+    /* and finally log the fact that we've closed the connection */
+    if (APLOG_CS_IS_LEVEL(c, mySrvFromConn(c), loglevel)) {
+        ap_log_cserror(APLOG_MARK, loglevel, 0, c, mySrvFromConn(c),
+                       "%sConnection closed to child %ld with %s shutdown "
+                       "(server %s)",
+                       logno, c->id, type,
+                       ssl_util_vhostid(c->pool, mySrvFromConn(c)));
+    }
+
+    /* deallocate the SSL connection */
+    if (sslconn->client_cert) {
+        X509_free(sslconn->client_cert);
+        sslconn->client_cert = NULL;
+    }
+    SSL_free(ssl);
+    sslconn->ssl = NULL;
+    filter_ctx->pssl = NULL; /* so filters know we've been shutdown */
+
+    if (abortive) {
+        /* prevent any further I/O */
+        c->aborted = 1;
+    }
+}
+
+static apr_status_t ssl_io_filter_cleanup(void *data)
+{
+    ssl_filter_ctx_t *filter_ctx = data;
+
+    if (filter_ctx->pssl) {
+        conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
+        SSLConnRec *sslconn = myConnConfig(c);
+
+        SSL_free(filter_ctx->pssl);
+        sslconn->ssl = filter_ctx->pssl = NULL;
+    }
+
+    return APR_SUCCESS;
+}
+
+/*
+ * The hook is NOT registered with ap_hook_process_connection. Instead, it is
+ * called manually from the churn () before it tries to read any data.
+ * There is some problem if I accept conn_rec *. Still investigating..
+ * Adv. if conn_rec * can be accepted is we can hook this function using the
+ * ap_hook_process_connection hook.
+ */
+
+/* Perform the SSL handshake (whether in client or server mode), if
+ * necessary, for the given connection. */
+static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
+{
+    conn_rec *c         = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
+    SSLConnRec *sslconn = myConnConfig(c);
+    SSLSrvConfigRec *sc;
+    X509 *cert;
+    int n;
+    int ssl_err;
+    long verify_result;
+    server_rec *server;
+
+    if (SSL_is_init_finished(filter_ctx->pssl)) {
+        return APR_SUCCESS;
+    }
+
+    server = sslconn->server;
+    if (sslconn->is_proxy) {
+#ifdef HAVE_TLSEXT
+        apr_ipsubnet_t *ip;
+#endif
+        const char *hostname_note = apr_table_get(c->notes,
+                                                  "proxy-request-hostname");
+        BOOL proxy_ssl_check_peer_ok = TRUE;
+        sc = mySrvConfig(server);
+
+#ifdef HAVE_TLSEXT
+        /*
+         * Enable SNI for backend requests. Make sure we don't do it for
+         * pure SSLv3 connections, and also prevent IP addresses
+         * from being included in the SNI extension. (OpenSSL would simply
+         * pass them on, but RFC 6066 is quite clear on this: "Literal
+         * IPv4 and IPv6 addresses are not permitted".)
+         */
+        if (hostname_note &&
+            sc->proxy->protocol != SSL_PROTOCOL_SSLV3 &&
+            apr_ipsubnet_create(&ip, hostname_note, NULL,
+                                c->pool) != APR_SUCCESS) {
+            if (SSL_set_tlsext_host_name(filter_ctx->pssl, hostname_note)) {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                              "SNI extension for SSL Proxy request set to '%s'",
+                              hostname_note);
+            } else {
+                ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(02002)
+                              "Failed to set SNI extension for SSL Proxy "
+                              "request to '%s'", hostname_note);
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_WARNING, server);
+            }
+       }
+#endif
+
+        if ((n = SSL_connect(filter_ctx->pssl)) <= 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02003)
+                          "SSL Proxy connect failed");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+            /* ensure that the SSL structures etc are freed, etc: */
+            ssl_filter_io_shutdown(filter_ctx, c, 1);
+            apr_table_setn(c->notes, "SSL_connect_rv", "err");
+            return MODSSL_ERROR_BAD_GATEWAY;
+        }
+
+        cert = SSL_get_peer_certificate(filter_ctx->pssl);
+
+        if (sc->proxy_ssl_check_peer_expire != SSL_ENABLED_FALSE) {
+            if (!cert
+                || (X509_cmp_current_time(
+                     X509_get_notBefore(cert)) >= 0)
+                || (X509_cmp_current_time(
+                     X509_get_notAfter(cert)) <= 0)) {
+                proxy_ssl_check_peer_ok = FALSE;
+                ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02004)
+                              "SSL Proxy: Peer certificate is expired");
+            }
+        }
+        if ((sc->proxy_ssl_check_peer_name != SSL_ENABLED_FALSE) &&
+            hostname_note) {
+            apr_table_unset(c->notes, "proxy-request-hostname");
+            if (!cert
+                || SSL_X509_match_name(c->pool, cert, hostname_note,
+                                       TRUE, server) == FALSE) {
+                proxy_ssl_check_peer_ok = FALSE;
+                ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02411)
+                              "SSL Proxy: Peer certificate does not match "
+                              "for hostname %s", hostname_note);
+            }
+        }
+        else if ((sc->proxy_ssl_check_peer_cn != SSL_ENABLED_FALSE) &&
+            hostname_note) {
+            const char *hostname;
+            int match = 0;
+
+            hostname = ssl_var_lookup(NULL, server, c, NULL,
+                                      "SSL_CLIENT_S_DN_CN");
+            apr_table_unset(c->notes, "proxy-request-hostname");
+
+            /* Do string match or simplest wildcard match if that
+             * fails. */
+            match = strcasecmp(hostname, hostname_note) == 0;
+            if (!match && strncmp(hostname, "*.", 2) == 0) {
+                const char *p = ap_strchr_c(hostname_note, '.');
+                
+                match = p && strcasecmp(p, hostname + 1) == 0;
+            }
+
+            if (!match) {
+                proxy_ssl_check_peer_ok = FALSE;
+                ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02005)
+                              "SSL Proxy: Peer certificate CN mismatch:"
+                              " Certificate CN: %s Requested hostname: %s",
+                              hostname, hostname_note);
+            }
+        }
+
+        if (cert) {
+            X509_free(cert);
+        }
+
+        if (proxy_ssl_check_peer_ok != TRUE) {
+            /* ensure that the SSL structures etc are freed, etc: */
+            ssl_filter_io_shutdown(filter_ctx, c, 1);
+            apr_table_setn(c->notes, "SSL_connect_rv", "err");
+            return HTTP_BAD_GATEWAY;
+        }
+
+        apr_table_setn(c->notes, "SSL_connect_rv", "ok");
+        return APR_SUCCESS;
+    }
+
+    if ((n = SSL_accept(filter_ctx->pssl)) <= 0) {
+        bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)
+                                     (filter_ctx->pbioRead->ptr);
+        bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)
+                                       (filter_ctx->pbioWrite->ptr);
+        apr_status_t rc = inctx->rc ? inctx->rc : outctx->rc ;
+        ssl_err = SSL_get_error(filter_ctx->pssl, n);
+
+        if (ssl_err == SSL_ERROR_ZERO_RETURN) {
+            /*
+             * The case where the connection was closed before any data
+             * was transferred. That's not a real error and can occur
+             * sporadically with some clients.
+             */
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, APLOGNO(02006)
+                         "SSL handshake stopped: connection was closed");
+        }
+        else if (ssl_err == SSL_ERROR_WANT_READ) {
+            /*
+             * This is in addition to what was present earlier. It is
+             * borrowed from openssl_state_machine.c [mod_tls].
+             * TBD.
+             */
+            outctx->rc = APR_EAGAIN;
+            return APR_EAGAIN;
+        }
+        else if (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL &&
+                 ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) {
+            /*
+             * The case where OpenSSL has recognized a HTTP request:
+             * This means the client speaks plain HTTP on our HTTPS port.
+             * ssl_io_filter_error will disable the ssl filters when it
+             * sees this status code.
+             */
+            return MODSSL_ERROR_HTTP_ON_HTTPS;
+        }
+        else if (ssl_err == SSL_ERROR_SYSCALL) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rc, c, APLOGNO(02007)
+                          "SSL handshake interrupted by system "
+                          "[Hint: Stop button pressed in browser?!]");
+        }
+        else /* if (ssl_err == SSL_ERROR_SSL) */ {
+            /*
+             * Log SSL errors and any unexpected conditions.
+             */
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, APLOGNO(02008)
+                          "SSL library error %d in handshake "
+                          "(server %s)", ssl_err,
+                          ssl_util_vhostid(c->pool, server));
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+
+        }
+        if (inctx->rc == APR_SUCCESS) {
+            inctx->rc = APR_EGENERAL;
+        }
+
+        ssl_filter_io_shutdown(filter_ctx, c, 1);
+        return inctx->rc;
+    }
+    sc = mySrvConfig(sslconn->server);
+
+    /*
+     * Check for failed client authentication
+     */
+    verify_result = SSL_get_verify_result(filter_ctx->pssl);
+
+    if ((verify_result != X509_V_OK) ||
+        sslconn->verify_error)
+    {
+        if (ssl_verify_error_is_optional(verify_result) &&
+            (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+        {
+            /* leaving this log message as an error for the moment,
+             * according to the mod_ssl docs:
+             * "level optional_no_ca is actually against the idea
+             *  of authentication (but can be used to establish
+             * SSL test pages, etc.)"
+             * optional_no_ca doesn't appear to work as advertised
+             * in 1.x
+             */
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02009)
+                          "SSL client authentication failed, "
+                          "accepting certificate based on "
+                          "\"SSLVerifyClient optional_no_ca\" "
+                          "configuration");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+        }
+        else {
+            const char *error = sslconn->verify_error ?
+                sslconn->verify_error :
+                X509_verify_cert_error_string(verify_result);
+
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02010)
+                         "SSL client authentication failed: %s",
+                         error ? error : "unknown");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
+
+            ssl_filter_io_shutdown(filter_ctx, c, 1);
+            return APR_ECONNABORTED;
+        }
+    }
+
+    /*
+     * Remember the peer certificate's DN
+     */
+    if ((cert = SSL_get_peer_certificate(filter_ctx->pssl))) {
+        if (sslconn->client_cert) {
+            X509_free(sslconn->client_cert);
+        }
+        sslconn->client_cert = cert;
+        sslconn->client_dn = NULL;
+    }
+
+    /*
+     * Make really sure that when a peer certificate
+     * is required we really got one... (be paranoid)
+     */
+    if ((sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE) &&
+        !sslconn->client_cert)
+    {
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02011)
+                      "No acceptable peer certificate available");
+
+        ssl_filter_io_shutdown(filter_ctx, c, 1);
+        return APR_ECONNABORTED;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_io_filter_input(ap_filter_t *f,
+                                        apr_bucket_brigade *bb,
+                                        ap_input_mode_t mode,
+                                        apr_read_type_e block,
+                                        apr_off_t readbytes)
+{
+    apr_status_t status;
+    bio_filter_in_ctx_t *inctx = f->ctx;
+    const char *start = inctx->buffer; /* start of block to return */
+    apr_size_t len = sizeof(inctx->buffer); /* length of block to return */
+    int is_init = (mode == AP_MODE_INIT);
+
+    if (f->c->aborted) {
+        /* XXX: Ok, if we aborted, we ARE at the EOS.  We also have
+         * aborted.  This 'double protection' is probably redundant,
+         * but also effective against just about anything.
+         */
+        apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(bb, bucket);
+        return APR_ECONNABORTED;
+    }
+
+    if (!inctx->ssl) {
+        SSLConnRec *sslconn = myConnConfig(f->c);
+        if (sslconn->non_ssl_request == NON_SSL_SEND_HDR_SEP) {
+            apr_bucket *bucket = apr_bucket_immortal_create(CRLF, 2, f->c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, bucket);
+            sslconn->non_ssl_request = NON_SSL_SET_ERROR_MSG;
+            return APR_SUCCESS;
+        }
+        return ap_get_brigade(f->next, bb, mode, block, readbytes);
+    }
+
+    /* XXX: we don't currently support anything other than these modes. */
+    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
+        mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
+        return APR_ENOTIMPL;
+    }
+
+    inctx->mode = mode;
+    inctx->block = block;
+
+    /* XXX: we could actually move ssl_io_filter_handshake to an
+     * ap_hook_process_connection but would still need to call it for
+     * AP_MODE_INIT for protocols that may upgrade the connection
+     * rather than have SSLEngine On configured.
+     */
+    if ((status = ssl_io_filter_handshake(inctx->filter_ctx)) != APR_SUCCESS) {
+        return ssl_io_filter_error(f, bb, status);
+    }
+
+    if (is_init) {
+        /* protocol module needs to handshake before sending
+         * data to client (e.g. NNTP or FTP)
+         */
+        return APR_SUCCESS;
+    }
+
+    if (inctx->mode == AP_MODE_READBYTES ||
+        inctx->mode == AP_MODE_SPECULATIVE) {
+        /* Protected from truncation, readbytes < MAX_SIZE_T
+         * FIXME: No, it's *not* protected.  -- jre */
+        if (readbytes < len) {
+            len = (apr_size_t)readbytes;
+        }
+        status = ssl_io_input_read(inctx, inctx->buffer, &len);
+    }
+    else if (inctx->mode == AP_MODE_GETLINE) {
+        const char *pos;
+
+        /* Satisfy the read directly out of the buffer if possible;
+         * invoking ssl_io_input_getline will mean the entire buffer
+         * is copied once (unnecessarily) for each GETLINE call. */
+        if (inctx->cbuf.length
+            && (pos = memchr(inctx->cbuf.value, APR_ASCII_LF,
+                             inctx->cbuf.length)) != NULL) {
+            start = inctx->cbuf.value;
+            len = 1 + pos - start; /* +1 to include LF */
+            /* Buffer contents now consumed. */
+            inctx->cbuf.value += len;
+            inctx->cbuf.length -= len;
+            status = APR_SUCCESS;
+        }
+        else {
+            /* Otherwise fall back to the hard way. */
+            status = ssl_io_input_getline(inctx, inctx->buffer, &len);
+        }
+    }
+    else {
+        /* We have no idea what you are talking about, so return an error. */
+        status = APR_ENOTIMPL;
+    }
+
+    /* It is possible for mod_ssl's BIO to be used outside of the
+     * direct control of mod_ssl's input or output filter -- notably,
+     * when mod_ssl initiates a renegotiation.  Switching the BIO mode
+     * back to "blocking" here ensures such operations don't fail with
+     * SSL_ERROR_WANT_READ. */
+    inctx->block = APR_BLOCK_READ;
+
+    /* Handle custom errors. */
+    if (status != APR_SUCCESS) {
+        return ssl_io_filter_error(f, bb, status);
+    }
+
+    /* Create a transient bucket out of the decrypted data. */
+    if (len > 0) {
+        apr_bucket *bucket =
+            apr_bucket_transient_create(start, len, f->c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(bb, bucket);
+    }
+
+#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+       /* By this point, Application-Layer Protocol Negotiation (ALPN) should be 
+        * completed (if our version of OpenSSL supports it). If we haven't already, 
+        * find out which protocol was decided upon and inform other modules 
+        * by calling alpn_proto_negotiated_hook. 
+        */
+       if (!inctx->alpn_finished) {
+               SSLConnRec *sslconn = myConnConfig(f->c);
+               const unsigned char *next_proto = NULL;
+               unsigned next_proto_len = 0;
+               int n;
+               
+               if (sslconn->alpn_negofns) {
+       #ifdef HAVE_TLS_ALPN
+                       SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
+                       ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
+                                                 APLOGNO(02306) "SSL ALPN negotiated protocol: '%*s'",
+                                                 next_proto_len, (const char*)next_proto);
+       #else
+                       SSL_get0_next_proto_negotiated(
+                                                                                  inctx->ssl, &next_proto, &next_proto_len);
+                       ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
+                                                 APLOGNO(02306) "SSL NPN negotiated protocol: '%*s'",
+                                                 next_proto_len, (const char*)next_proto);
+       #endif
+                       for (n = 0; n < sslconn->alpn_negofns->nelts; n++) {
+                               ssl_alpn_proto_negotiated fn =
+                               APR_ARRAY_IDX(sslconn->alpn_negofns, n, ssl_alpn_proto_negotiated);
+                               
+                               if (fn(f->c, (const char *)next_proto, next_proto_len) == DONE)
+                               break;
+                       }
+               }
+               inctx->alpn_finished = 1;
+       }
+#endif
+
+    return APR_SUCCESS;
+}
+
+
+/* ssl_io_filter_output() produces one SSL/TLS message per bucket
+ * passed down the output filter stack.  This results in a high
+ * overhead (network packets) for any output comprising many small
+ * buckets.  SSI page applied through the HTTP chunk filter, for
+ * example, may produce many brigades containing small buckets -
+ * [chunk-size CRLF] [chunk-data] [CRLF].
+ *
+ * The coalescing filter merges many small buckets into larger buckets
+ * where possible, allowing the SSL I/O output filter to handle them
+ * more efficiently. */
+
+#define COALESCE_BYTES (2048)
+
+struct coalesce_ctx {
+    char buffer[COALESCE_BYTES];
+    apr_size_t bytes; /* number of bytes of buffer used. */
+};
+
+static apr_status_t ssl_io_filter_coalesce(ap_filter_t *f,
+                                           apr_bucket_brigade *bb)
+{
+    apr_bucket *e, *last = NULL;
+    apr_size_t bytes = 0;
+    struct coalesce_ctx *ctx = f->ctx;
+    unsigned count = 0;
+
+    /* The brigade consists of zero-or-more small data buckets which
+     * can be coalesced (the prefix), followed by the remainder of the
+     * brigade.
+     *
+     * Find the last bucket - if any - of that prefix.  count gives
+     * the number of buckets in the prefix.  The "prefix" must contain
+     * only data buckets with known length, and must be of a total
+     * size which fits into the buffer.
+     *
+     * N.B.: The process here could be repeated throughout the brigade
+     * (coalesce any run of consecutive data buckets) but this would
+     * add significant complexity, particularly to memory
+     * management. */
+    for (e = APR_BRIGADE_FIRST(bb);
+         e != APR_BRIGADE_SENTINEL(bb)
+             && !APR_BUCKET_IS_METADATA(e)
+             && e->length != (apr_size_t)-1
+             && e->length < COALESCE_BYTES
+             && (bytes + e->length) < COALESCE_BYTES
+             && (ctx == NULL
+                 || bytes + ctx->bytes + e->length < COALESCE_BYTES);
+         e = APR_BUCKET_NEXT(e)) {
+        last = e;
+        if (e->length) count++; /* don't count zero-length buckets */
+        bytes += e->length;
+    }
+
+    /* Coalesce the prefix, if:
+     * a) more than one bucket is found to coalesce, or
+     * b) the brigade contains only a single data bucket, or
+     * c)
+     */
+    if (bytes > 0
+        && (count > 1
+            || (count == 1 && APR_BUCKET_NEXT(last) == APR_BRIGADE_SENTINEL(bb)))) {
+        /* If coalescing some bytes, ensure a context has been
+         * created. */
+        if (!ctx) {
+            f->ctx = ctx = apr_palloc(f->c->pool, sizeof *ctx);
+            ctx->bytes = 0;
+        }
+
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+                      "coalesce: have %" APR_SIZE_T_FMT " bytes, "
+                      "adding %" APR_SIZE_T_FMT " more", ctx->bytes, bytes);
+
+        /* Iterate through the prefix segment.  For non-fatal errors
+         * in this loop it is safe to break out and fall back to the
+         * normal path of sending the buffer + remaining buckets in
+         * brigade.  */
+        e = APR_BRIGADE_FIRST(bb);
+        while (e != last) {
+            apr_size_t len;
+            const char *data;
+            apr_bucket *next;
+
+            if (APR_BUCKET_IS_METADATA(e)
+                || e->length == (apr_size_t)-1) {
+                ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(02012)
+                              "unexpected bucket type during coalesce");
+                break; /* non-fatal error; break out */
+            }
+
+            if (e->length) {
+                apr_status_t rv;
+
+                /* A blocking read should be fine here for a
+                 * known-length data bucket, rather than the usual
+                 * non-block/flush/block.  */
+                rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+                if (rv) {
+                    ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(02013)
+                                  "coalesce failed to read from data bucket");
+                    return AP_FILTER_ERROR;
+                }
+
+                /* Be paranoid. */
+                if (len > sizeof ctx->buffer
+                    || (len + ctx->bytes > sizeof ctx->buffer)) {
+                    ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(02014)
+                                  "unexpected coalesced bucket data length");
+                    break; /* non-fatal error; break out */
+                }
+
+                memcpy(ctx->buffer + ctx->bytes, data, len);
+                ctx->bytes += len;
+            }
+
+            next = APR_BUCKET_NEXT(e);
+            apr_bucket_delete(e);
+            e = next;
+        }
+    }
+
+    if (APR_BRIGADE_EMPTY(bb)) {
+        /* If the brigade is now empty, our work here is done. */
+        return APR_SUCCESS;
+    }
+
+    /* If anything remains in the brigade, it must now be passed down
+     * the filter stack, first prepending anything that has been
+     * coalesced. */
+    if (ctx && ctx->bytes) {
+        apr_bucket *e;
+
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+                      "coalesce: passing on %" APR_SIZE_T_FMT " bytes", ctx->bytes);
+
+        e = apr_bucket_transient_create(ctx->buffer, ctx->bytes, bb->bucket_alloc);
+        APR_BRIGADE_INSERT_HEAD(bb, e);
+        ctx->bytes = 0; /* buffer now emptied. */
+    }
+
+    return ap_pass_brigade(f->next, bb);
+}
+
+static apr_status_t ssl_io_filter_output(ap_filter_t *f,
+                                         apr_bucket_brigade *bb)
+{
+    apr_status_t status = APR_SUCCESS;
+    ssl_filter_ctx_t *filter_ctx = f->ctx;
+    bio_filter_in_ctx_t *inctx;
+    bio_filter_out_ctx_t *outctx;
+    apr_read_type_e rblock = APR_NONBLOCK_READ;
+
+    if (f->c->aborted) {
+        apr_brigade_cleanup(bb);
+        return APR_ECONNABORTED;
+    }
+
+    if (!filter_ctx->pssl) {
+        /* ssl_filter_io_shutdown was called */
+        return ap_pass_brigade(f->next, bb);
+    }
+
+    inctx = (bio_filter_in_ctx_t *)filter_ctx->pbioRead->ptr;
+    outctx = (bio_filter_out_ctx_t *)filter_ctx->pbioWrite->ptr;
+
+    /* When we are the writer, we must initialize the inctx
+     * mode so that we block for any required ssl input, because
+     * output filtering is always nonblocking.
+     */
+    inctx->mode = AP_MODE_READBYTES;
+    inctx->block = APR_BLOCK_READ;
+
+    if ((status = ssl_io_filter_handshake(filter_ctx)) != APR_SUCCESS) {
+        return ssl_io_filter_error(f, bb, status);
+    }
+
+    while (!APR_BRIGADE_EMPTY(bb)) {
+        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
+
+        /* If it is a flush or EOS, we need to pass this down.
+         * These types do not require translation by OpenSSL.
+         */
+        if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
+            if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
+                status = outctx->rc;
+                break;
+            }
+
+            if (APR_BUCKET_IS_EOS(bucket)) {
+                /*
+                 * By definition, nothing can come after EOS.
+                 * which also means we can pass the rest of this brigade
+                 * without creating a new one since it only contains the
+                 * EOS bucket.
+                 */
+
+                if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
+                    return status;
+                }
+                break;
+            }
+            else {
+                /* bio_filter_out_flush() already passed down a flush bucket
+                 * if there was any data to be flushed.
+                 */
+                apr_bucket_delete(bucket);
+            }
+        }
+        else if (AP_BUCKET_IS_EOC(bucket)) {
+            /* The EOC bucket indicates connection closure, so SSL
+             * shutdown must now be performed.  */
+            ssl_filter_io_shutdown(filter_ctx, f->c, 0);
+            if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
+                return status;
+            }
+            break;
+        }
+        else {
+            /* filter output */
+            const char *data;
+            apr_size_t len;
+
+            status = apr_bucket_read(bucket, &data, &len, rblock);
+
+            if (APR_STATUS_IS_EAGAIN(status)) {
+                /* No data available: flush... */
+                if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
+                    status = outctx->rc;
+                    break;
+                }
+                rblock = APR_BLOCK_READ;
+                continue; /* and try again with a blocking read. */
+            }
+
+            rblock = APR_NONBLOCK_READ;
+
+            if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
+                break;
+            }
+
+            status = ssl_filter_write(f, data, len);
+            apr_bucket_delete(bucket);
+
+            if (status != APR_SUCCESS) {
+                break;
+            }
+        }
+    }
+
+    return status;
+}
+
+struct modssl_buffer_ctx {
+    apr_bucket_brigade *bb;
+};
+
+int ssl_io_buffer_fill(request_rec *r, apr_size_t maxlen)
+{
+    conn_rec *c = r->connection;
+    struct modssl_buffer_ctx *ctx;
+    apr_bucket_brigade *tempb;
+    apr_off_t total = 0; /* total length buffered */
+    int eos = 0; /* non-zero once EOS is seen */
+
+    /* Create the context which will be passed to the input filter;
+     * containing a setaside pool and a brigade which constrain the
+     * lifetime of the buffered data. */
+    ctx = apr_palloc(r->pool, sizeof *ctx);
+    ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
+
+    /* ... and a temporary brigade. */
+    tempb = apr_brigade_create(r->pool, c->bucket_alloc);
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, c, "filling buffer, max size "
+                  "%" APR_SIZE_T_FMT " bytes", maxlen);
+
+    do {
+        apr_status_t rv;
+        apr_bucket *e, *next;
+
+        /* The request body is read from the protocol-level input
+         * filters; the buffering filter will reinject it from that
+         * level, allowing content/resource filters to run later, if
+         * necessary. */
+
+        rv = ap_get_brigade(r->proto_input_filters, tempb, AP_MODE_READBYTES,
+                            APR_BLOCK_READ, 8192);
+        if (rv) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02015)
+                          "could not read request body for SSL buffer");
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        /* Iterate through the returned brigade: setaside each bucket
+         * into the context's pool and move it into the brigade. */
+        for (e = APR_BRIGADE_FIRST(tempb);
+             e != APR_BRIGADE_SENTINEL(tempb) && !eos; e = next) {
+            const char *data;
+            apr_size_t len;
+
+            next = APR_BUCKET_NEXT(e);
+
+            if (APR_BUCKET_IS_EOS(e)) {
+                eos = 1;
+            } else if (!APR_BUCKET_IS_METADATA(e)) {
+                rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+                if (rv != APR_SUCCESS) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02016)
+                                  "could not read bucket for SSL buffer");
+                    return HTTP_INTERNAL_SERVER_ERROR;
+                }
+                total += len;
+            }
+
+            rv = apr_bucket_setaside(e, r->pool);
+            if (rv != APR_SUCCESS) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02017)
+                              "could not setaside bucket for SSL buffer");
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            APR_BUCKET_REMOVE(e);
+            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+        }
+
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, c,
+                      "total of %" APR_OFF_T_FMT " bytes in buffer, eos=%d",
+                      total, eos);
+
+        /* Fail if this exceeds the maximum buffer size. */
+        if (total > maxlen) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02018)
+                          "request body exceeds maximum size (%" APR_SIZE_T_FMT
+                          ") for SSL buffer", maxlen);
+            return HTTP_REQUEST_ENTITY_TOO_LARGE;
+        }
+
+    } while (!eos);
+
+    apr_brigade_destroy(tempb);
+
+    /* After consuming all protocol-level input, remove all protocol-level
+     * filters.  It should strictly only be necessary to remove filters
+     * at exactly ftype == AP_FTYPE_PROTOCOL, since this filter will
+     * precede all > AP_FTYPE_PROTOCOL anyway. */
+    while (r->proto_input_filters->frec->ftype < AP_FTYPE_CONNECTION) {
+        ap_remove_input_filter(r->proto_input_filters);
+    }
+
+    /* Insert the filter which will supply the buffered content. */
+    ap_add_input_filter(ssl_io_buffer, ctx, r, c);
+
+    return 0;
+}
+
+/* This input filter supplies the buffered request body to the caller
+ * from the brigade stored in f->ctx.  Note that the placement of this
+ * filter in the filter stack is important; it must be the first
+ * r->proto_input_filter; lower-typed filters will not be preserved
+ * across internal redirects (see PR 43738).  */
+static apr_status_t ssl_io_filter_buffer(ap_filter_t *f,
+                                         apr_bucket_brigade *bb,
+                                         ap_input_mode_t mode,
+                                         apr_read_type_e block,
+                                         apr_off_t bytes)
+{
+    struct modssl_buffer_ctx *ctx = f->ctx;
+    apr_status_t rv;
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+                  "read from buffered SSL brigade, mode %d, "
+                  "%" APR_OFF_T_FMT " bytes",
+                  mode, bytes);
+
+    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
+        return APR_ENOTIMPL;
+    }
+
+    if (APR_BRIGADE_EMPTY(ctx->bb)) {
+        /* Suprisingly (and perhaps, wrongly), the request body can be
+         * pulled from the input filter stack more than once; a
+         * handler may read it, and ap_discard_request_body() will
+         * attempt to do so again after *every* request.  So input
+         * filters must be prepared to give up an EOS if invoked after
+         * initially reading the request. The HTTP_IN filter does this
+         * with its ->eos_sent flag. */
+
+        APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(f->c->bucket_alloc));
+        return APR_SUCCESS;
+    }
+
+    if (mode == AP_MODE_READBYTES) {
+        apr_bucket *e;
+
+        /* Partition the buffered brigade. */
+        rv = apr_brigade_partition(ctx->bb, bytes, &e);
+        if (rv && rv != APR_INCOMPLETE) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(02019)
+                          "could not partition buffered SSL brigade");
+            ap_remove_input_filter(f);
+            return rv;
+        }
+
+        /* If the buffered brigade contains less then the requested
+         * length, just pass it all back. */
+        if (rv == APR_INCOMPLETE) {
+            APR_BRIGADE_CONCAT(bb, ctx->bb);
+        } else {
+            apr_bucket *d = APR_BRIGADE_FIRST(ctx->bb);
+
+            e = APR_BUCKET_PREV(e);
+
+            /* Unsplice the partitioned segment and move it into the
+             * passed-in brigade; no convenient way to do this with
+             * the APR_BRIGADE_* macros. */
+            APR_RING_UNSPLICE(d, e, link);
+            APR_RING_SPLICE_HEAD(&bb->list, d, e, apr_bucket, link);
+
+            APR_BRIGADE_CHECK_CONSISTENCY(bb);
+            APR_BRIGADE_CHECK_CONSISTENCY(ctx->bb);
+        }
+    }
+    else {
+        /* Split a line into the passed-in brigade. */
+        rv = apr_brigade_split_line(bb, ctx->bb, block, bytes);
+
+        if (rv) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(02020)
+                          "could not split line from buffered SSL brigade");
+            ap_remove_input_filter(f);
+            return rv;
+        }
+    }
+
+    if (APR_BRIGADE_EMPTY(ctx->bb)) {
+        apr_bucket *e = APR_BRIGADE_LAST(bb);
+
+        /* Ensure that the brigade is terminated by an EOS if the
+         * buffered request body has been entirely consumed. */
+        if (e == APR_BRIGADE_SENTINEL(bb) || !APR_BUCKET_IS_EOS(e)) {
+            e = apr_bucket_eos_create(f->c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, e);
+        }
+
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c,
+                      "buffered SSL brigade exhausted");
+        /* Note that the filter must *not* be removed here; it may be
+         * invoked again, see comment above. */
+    }
+
+    return APR_SUCCESS;
+}
+
+/* The request_rec pointer is passed in here only to ensure that the
+ * filter chain is modified correctly when doing a TLS upgrade.  It
+ * must *not* be used otherwise. */
+static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c,
+                                    request_rec *r, SSL *ssl)
+{
+    bio_filter_in_ctx_t *inctx;
+
+    inctx = apr_palloc(c->pool, sizeof(*inctx));
+
+    filter_ctx->pInputFilter = ap_add_input_filter(ssl_io_filter, inctx, r, c);
+
+    filter_ctx->pbioRead = BIO_new(&bio_filter_in_method);
+    filter_ctx->pbioRead->ptr = (void *)inctx;
+
+    inctx->ssl = ssl;
+    inctx->bio_out = filter_ctx->pbioWrite;
+    inctx->f = filter_ctx->pInputFilter;
+    inctx->rc = APR_SUCCESS;
+    inctx->mode = AP_MODE_READBYTES;
+    inctx->cbuf.length = 0;
+    inctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);
+    inctx->block = APR_BLOCK_READ;
+    inctx->pool = c->pool;
+    inctx->filter_ctx = filter_ctx;
+    inctx->alpn_finished = 0;
+}
+
+/* The request_rec pointer is passed in here only to ensure that the
+ * filter chain is modified correctly when doing a TLS upgrade.  It
+ * must *not* be used otherwise. */
+void ssl_io_filter_init(conn_rec *c, request_rec *r, SSL *ssl)
+{
+    ssl_filter_ctx_t *filter_ctx;
+
+    filter_ctx = apr_palloc(c->pool, sizeof(ssl_filter_ctx_t));
+
+    filter_ctx->config          = myConnConfig(c);
+
+    ap_add_output_filter(ssl_io_coalesce, NULL, r, c);
+
+    filter_ctx->pOutputFilter   = ap_add_output_filter(ssl_io_filter,
+                                                       filter_ctx, r, c);
+
+    filter_ctx->pbioWrite       = BIO_new(&bio_filter_out_method);
+    filter_ctx->pbioWrite->ptr  = (void *)bio_filter_out_ctx_new(filter_ctx, c);
+
+    /* write is non blocking for the benefit of async mpm */
+    if (c->cs) {
+        BIO_set_nbio(filter_ctx->pbioWrite, 1);
+    }
+
+    ssl_io_input_add_filter(filter_ctx, c, r, ssl);
+
+    SSL_set_bio(ssl, filter_ctx->pbioRead, filter_ctx->pbioWrite);
+    filter_ctx->pssl            = ssl;
+
+    apr_pool_cleanup_register(c->pool, (void*)filter_ctx,
+                              ssl_io_filter_cleanup, apr_pool_cleanup_null);
+
+    if (APLOG_CS_IS_LEVEL(c, mySrvFromConn(c), APLOG_TRACE4)) {
+        BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb);
+        BIO_set_callback_arg(SSL_get_rbio(ssl), (void *)ssl);
+    }
+
+    return;
+}
+
+void ssl_io_filter_register(apr_pool_t *p)
+{
+    ap_register_input_filter  (ssl_io_filter, ssl_io_filter_input,  NULL, AP_FTYPE_CONNECTION + 5);
+    ap_register_output_filter (ssl_io_coalesce, ssl_io_filter_coalesce, NULL, AP_FTYPE_CONNECTION + 4);
+    ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5);
+
+    ap_register_input_filter  (ssl_io_buffer, ssl_io_filter_buffer, NULL, AP_FTYPE_PROTOCOL);
+
+    return;
+}
+
+/*  _________________________________________________________________
+**
+**  I/O Data Debugging
+**  _________________________________________________________________
+*/
+
+#define DUMP_WIDTH 16
+
+static void ssl_io_data_dump(server_rec *srvr,
+                             const char *s,
+                             long len)
+{
+    char buf[256];
+    char tmp[64];
+    int i, j, rows, trunc;
+    unsigned char ch;
+
+    trunc = 0;
+    for(; (len > 0) && ((s[len-1] == ' ') || (s[len-1] == '\0')); len--)
+        trunc++;
+    rows = (len / DUMP_WIDTH);
+    if ((rows * DUMP_WIDTH) < len)
+        rows++;
+    ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, srvr,
+            "+-------------------------------------------------------------------------+");
+    for(i = 0 ; i< rows; i++) {
+#if APR_CHARSET_EBCDIC
+        char ebcdic_text[DUMP_WIDTH];
+        j = DUMP_WIDTH;
+        if ((i * DUMP_WIDTH + j) > len)
+            j = len % DUMP_WIDTH;
+        if (j == 0)
+            j = DUMP_WIDTH;
+        memcpy(ebcdic_text,(char *)(s) + i * DUMP_WIDTH, j);
+        ap_xlate_proto_from_ascii(ebcdic_text, j);
+#endif /* APR_CHARSET_EBCDIC */
+        apr_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH);
+        apr_cpystrn(buf, tmp, sizeof(buf));
+        for (j = 0; j < DUMP_WIDTH; j++) {
+            if (((i * DUMP_WIDTH) + j) >= len)
+                apr_cpystrn(buf+strlen(buf), "   ", sizeof(buf)-strlen(buf));
+            else {
+                ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
+                apr_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' ');
+                apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
+            }
+        }
+        apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+        for (j = 0; j < DUMP_WIDTH; j++) {
+            if (((i * DUMP_WIDTH) + j) >= len)
+                apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+            else {
+                ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
+#if APR_CHARSET_EBCDIC
+                apr_snprintf(tmp, sizeof(tmp), "%c", (ch >= 0x20 && ch <= 0x7F) ? ebcdic_text[j] : '.');
+#else /* APR_CHARSET_EBCDIC */
+                apr_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
+#endif /* APR_CHARSET_EBCDIC */
+                apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
+            }
+        }
+        apr_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf));
+        ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, srvr,
+                     "%s", buf);
+    }
+    if (trunc > 0)
+        ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, srvr,
+                "| %04ld - <SPACES/NULS>", len + trunc);
+    ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, srvr,
+            "+-------------------------------------------------------------------------+");
+    return;
+}
+
+long ssl_io_data_cb(BIO *bio, int cmd,
+                    const char *argp,
+                    int argi, long argl, long rc)
+{
+    SSL *ssl;
+    conn_rec *c;
+    server_rec *s;
+
+    if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL)
+        return rc;
+    if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
+        return rc;
+    s = mySrvFromConn(c);
+
+    if (   cmd == (BIO_CB_WRITE|BIO_CB_RETURN)
+        || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) {
+        if (rc >= 0) {
+            ap_log_cserror(APLOG_MARK, APLOG_TRACE4, 0, c, s,
+                    "%s: %s %ld/%d bytes %s BIO#%pp [mem: %pp] %s",
+                    SSL_LIBRARY_NAME,
+                    (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+                    rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
+                    bio, argp,
+                    (argp != NULL ? "(BIO dump follows)" : "(Oops, no memory buffer?)"));
+            if ((argp != NULL) && APLOG_CS_IS_LEVEL(c, s, APLOG_TRACE7))
+                ssl_io_data_dump(s, argp, rc);
+        }
+        else {
+            ap_log_cserror(APLOG_MARK, APLOG_TRACE4, 0, c, s,
+                    "%s: I/O error, %d bytes expected to %s on BIO#%pp [mem: %pp]",
+                    SSL_LIBRARY_NAME, argi,
+                    (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+                    bio, argp);
+        }
+    }
+    return rc;
+}
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_kernel.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_kernel.c
new file mode 100644 (file)
index 0000000..387c0bf
--- /dev/null
@@ -0,0 +1,2419 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_kernel.c
+ *  The SSL engine kernel
+ */
+                             /* ``It took me fifteen years to discover
+                                  I had no talent for programming, but
+                                  I couldn't give it up because by that
+                                  time I was too famous.''
+                                            -- Unknown                */
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "util_md5.h"
+
+static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
+#ifdef HAVE_TLSEXT
+static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s);
+#endif
+
+#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"
+#define UPGRADE_HEADER "Upgrade: TLS/1.0, HTTP/1.1"
+#define CONNECTION_HEADER "Connection: Upgrade"
+
+/* Perform an upgrade-to-TLS for the given request, per RFC 2817. */
+static apr_status_t upgrade_connection(request_rec *r)
+{
+    struct conn_rec *conn = r->connection;
+    apr_bucket_brigade *bb;
+    SSLConnRec *sslconn;
+    apr_status_t rv;
+    SSL *ssl;
+
+    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02028)
+                  "upgrading connection to TLS");
+
+    bb = apr_brigade_create(r->pool, conn->bucket_alloc);
+
+    rv = ap_fputstrs(conn->output_filters, bb, SWITCH_STATUS_LINE, CRLF,
+                     UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL);
+    if (rv == APR_SUCCESS) {
+        APR_BRIGADE_INSERT_TAIL(bb,
+                                apr_bucket_flush_create(conn->bucket_alloc));
+        rv = ap_pass_brigade(conn->output_filters, bb);
+    }
+
+    if (rv) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02029)
+                      "failed to send 101 interim response for connection "
+                      "upgrade");
+        return rv;
+    }
+
+    ssl_init_ssl_connection(conn, r);
+
+    sslconn = myConnConfig(conn);
+    ssl = sslconn->ssl;
+
+    /* Perform initial SSL handshake. */
+    SSL_set_accept_state(ssl);
+    SSL_do_handshake(ssl);
+
+    if (SSL_get_state(ssl) != SSL_ST_OK) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02030)
+                      "TLS upgrade handshake failed: not accepted by client!?");
+
+        return APR_ECONNABORTED;
+    }
+
+    return APR_SUCCESS;
+}
+
+/* Perform a speculative (and non-blocking) read from the connection
+ * filters for the given request, to determine whether there is any
+ * pending data to read.  Return non-zero if there is, else zero. */
+static int has_buffered_data(request_rec *r)
+{
+    apr_bucket_brigade *bb;
+    apr_off_t len;
+    apr_status_t rv;
+    int result;
+
+    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+    rv = ap_get_brigade(r->connection->input_filters, bb, AP_MODE_SPECULATIVE,
+                        APR_NONBLOCK_READ, 1);
+    result = rv == APR_SUCCESS
+        && apr_brigade_length(bb, 1, &len) == APR_SUCCESS
+        && len > 0;
+
+    apr_brigade_destroy(bb);
+
+    return result;
+}
+
+/*
+ *  Post Read Request Handler
+ */
+int ssl_hook_ReadReq(request_rec *r)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+    SSLConnRec *sslconn;
+    const char *upgrade;
+#ifdef HAVE_TLSEXT
+    const char *servername;
+#endif
+    SSL *ssl;
+
+    /* Perform TLS upgrade here if "SSLEngine optional" is configured,
+     * SSL is not already set up for this connection, and the client
+     * has sent a suitable Upgrade header. */
+    if (sc->enabled == SSL_ENABLED_OPTIONAL && !myConnConfig(r->connection)
+        && (upgrade = apr_table_get(r->headers_in, "Upgrade")) != NULL
+        && ap_find_token(r->pool, upgrade, "TLS/1.0")) {
+        if (upgrade_connection(r)) {
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    sslconn = myConnConfig(r->connection);
+    if (!sslconn) {
+        return DECLINED;
+    }
+
+    if (sslconn->non_ssl_request == NON_SSL_SET_ERROR_MSG) {
+        apr_table_setn(r->notes, "error-notes",
+                       "Reason: You're speaking plain HTTP to an SSL-enabled "
+                       "server port.<br />\n Instead use the HTTPS scheme to "
+                       "access this URL, please.<br />\n");
+
+        /* Now that we have caught this error, forget it. we are done
+         * with using SSL on this request.
+         */
+        sslconn->non_ssl_request = NON_SSL_OK;
+
+        return HTTP_BAD_REQUEST;
+    }
+
+    /*
+     * Get the SSL connection structure and perform the
+     * delayed interlinking from SSL back to request_rec
+     */
+    ssl = sslconn->ssl;
+    if (!ssl) {
+        return DECLINED;
+    }
+#ifdef HAVE_TLSEXT
+    /*
+     * Perform SNI checks only on the initial request.  In particular,
+     * if these checks detect a problem, the checks shouldn't return an
+     * error again when processing an ErrorDocument redirect for the
+     * original problem.
+     */
+    if (r->proxyreq != PROXYREQ_PROXY && ap_is_initial_req(r)) {
+        if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
+            char *host, *scope_id;
+            apr_port_t port;
+            apr_status_t rv;
+
+            /*
+             * The SNI extension supplied a hostname. So don't accept requests
+             * with either no hostname or a different hostname as this could
+             * cause us to end up in a different virtual host as the one that
+             * was used for the handshake causing different SSL parameters to
+             * be applied as SSLProtocol, SSLCACertificateFile/Path and
+             * SSLCADNRequestFile/Path cannot be renegotiated (SSLCA* due
+             * to current limitations in OpenSSL, see
+             * http://mail-archives.apache.org/mod_mbox/httpd-dev/200806.mbox/%3C48592955.2090303@velox.ch%3E
+             * and
+             * http://mail-archives.apache.org/mod_mbox/httpd-dev/201312.mbox/%3CCAKQ1sVNpOrdiBm-UPw1hEdSN7YQXRRjeaT-MCWbW_7mN%3DuFiOw%40mail.gmail.com%3E
+             * )
+             */
+            if (!r->hostname) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02031)
+                            "Hostname %s provided via SNI, but no hostname"
+                            " provided in HTTP request", servername);
+                return HTTP_BAD_REQUEST;
+            }
+            rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool);
+            if (rv != APR_SUCCESS || scope_id) {
+                return HTTP_BAD_REQUEST;
+            }
+            if (strcasecmp(host, servername)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02032)
+                            "Hostname %s provided via SNI and hostname %s provided"
+                            " via HTTP are different", servername, host);
+                return HTTP_BAD_REQUEST;
+            }
+        }
+        else if (((sc->strict_sni_vhost_check == SSL_ENABLED_TRUE)
+                 || (mySrvConfig(sslconn->server))->strict_sni_vhost_check
+                    == SSL_ENABLED_TRUE)
+                 && r->connection->vhost_lookup_data) {
+            /*
+             * We are using a name based configuration here, but no hostname was
+             * provided via SNI. Don't allow that if are requested to do strict
+             * checking. Check whether this strict checking was set up either in the
+             * server config we used for handshaking or in our current server.
+             * This should avoid insecure configuration by accident.
+             */
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02033)
+                         "No hostname was provided via SNI for a name based"
+                         " virtual host");
+            apr_table_setn(r->notes, "error-notes",
+                           "Reason: The client software did not provide a "
+                           "hostname using Server Name Indication (SNI), "
+                           "which is required to access this server.<br />\n");
+            return HTTP_FORBIDDEN;
+        }
+    }
+#endif
+    SSL_set_app_data2(ssl, r);
+
+    /*
+     * Log information about incoming HTTPS requests
+     */
+    if (APLOGrinfo(r) && ap_is_initial_req(r)) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02034)
+                     "%s HTTPS request received for child %ld (server %s)",
+                     (r->connection->keepalives <= 0 ?
+                     "Initial (No.1)" :
+                     apr_psprintf(r->pool, "Subsequent (No.%d)",
+                                  r->connection->keepalives+1)),
+                     r->connection->id,
+                     ssl_util_vhostid(r->pool, r->server));
+    }
+
+    /* SetEnvIf ssl-*-shutdown flags can only be per-server,
+     * so they won't change across keepalive requests
+     */
+    if (sslconn->shutdown_type == SSL_SHUTDOWN_TYPE_UNSET) {
+        ssl_configure_env(r, sslconn);
+    }
+
+    return DECLINED;
+}
+
+/*
+ * Move SetEnvIf information from request_rec to conn_rec/BUFF
+ * to allow the close connection handler to use them.
+ */
+
+static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn)
+{
+    int i;
+    const apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
+    const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
+
+    sslconn->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD;
+
+    for (i = 0; i < arr->nelts; i++) {
+        const char *key = elts[i].key;
+
+        switch (*key) {
+          case 's':
+            /* being case-sensitive here.
+             * and not checking for the -shutdown since these are the only
+             * SetEnvIf "flags" we support
+             */
+            if (!strncmp(key+1, "sl-", 3)) {
+                key += 4;
+                if (!strncmp(key, "unclean", 7)) {
+                    sslconn->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                }
+                else if (!strncmp(key, "accurate", 8)) {
+                    sslconn->shutdown_type = SSL_SHUTDOWN_TYPE_ACCURATE;
+                }
+                return; /* should only ever be one ssl-*-shutdown */
+            }
+            break;
+        }
+    }
+}
+
+/*
+ *  Access Handler
+ */
+int ssl_hook_Access(request_rec *r)
+{
+    SSLDirConfigRec *dc         = myDirConfig(r);
+    SSLSrvConfigRec *sc         = mySrvConfig(r->server);
+    SSLConnRec *sslconn         = myConnConfig(r->connection);
+    SSL *ssl                    = sslconn ? sslconn->ssl : NULL;
+    server_rec *handshakeserver = sslconn ? sslconn->server : NULL;
+    SSL_CTX *ctx = NULL;
+    apr_array_header_t *requires;
+    ssl_require_t *ssl_requires;
+    int ok, i;
+    BOOL renegotiate = FALSE, renegotiate_quick = FALSE;
+    X509 *cert;
+    X509 *peercert;
+    X509_STORE *cert_store = NULL;
+    X509_STORE_CTX cert_store_ctx;
+    STACK_OF(SSL_CIPHER) *cipher_list_old = NULL, *cipher_list = NULL;
+    const SSL_CIPHER *cipher = NULL;
+    int depth, verify_old, verify, n;
+
+    if (ssl) {
+        ctx = SSL_get_SSL_CTX(ssl);
+    }
+
+    /*
+     * Support for SSLRequireSSL directive
+     */
+    if (dc->bSSLRequired && !ssl) {
+        if (sc->enabled == SSL_ENABLED_OPTIONAL) {
+            /* This vhost was configured for optional SSL, just tell the
+             * client that we need to upgrade.
+             */
+            apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+            apr_table_setn(r->err_headers_out, "Connection", "Upgrade");
+
+            return HTTP_UPGRADE_REQUIRED;
+        }
+
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02219)
+                      "access to %s failed, reason: %s",
+                      r->filename, "SSL connection required");
+
+        /* remember forbidden access for strict require option */
+        apr_table_setn(r->notes, "ssl-access-forbidden", "1");
+
+        return HTTP_FORBIDDEN;
+    }
+
+    /*
+     * Check to see whether SSL is in use; if it's not, then no
+     * further access control checks are relevant.  (the test for
+     * sc->enabled is probably strictly unnecessary)
+     */
+    if (sc->enabled == SSL_ENABLED_FALSE || !ssl) {
+        return DECLINED;
+    }
+
+#ifdef HAVE_SRP
+    /*
+     * Support for per-directory reconfigured SSL connection parameters
+     *
+     * We do not force any renegotiation if the user is already authenticated
+     * via SRP.
+     *
+     */
+    if (SSL_get_srp_username(ssl)) {
+        return DECLINED;
+    }
+#endif
+
+    /*
+     * Support for per-directory reconfigured SSL connection parameters.
+     *
+     * This is implemented by forcing an SSL renegotiation with the
+     * reconfigured parameter suite. But Apache's internal API processing
+     * makes our life very hard here, because when internal sub-requests occur
+     * we nevertheless should avoid multiple unnecessary SSL handshakes (they
+     * require extra network I/O and especially time to perform).
+     *
+     * But the optimization for filtering out the unnecessary handshakes isn't
+     * obvious and trivial.  Especially because while Apache is in its
+     * sub-request processing the client could force additional handshakes,
+     * too. And these take place perhaps without our notice. So the only
+     * possibility is to explicitly _ask_ OpenSSL whether the renegotiation
+     * has to be performed or not. It has to performed when some parameters
+     * which were previously known (by us) are not those we've now
+     * reconfigured (as known by OpenSSL) or (in optimized way) at least when
+     * the reconfigured parameter suite is stronger (more restrictions) than
+     * the currently active one.
+     */
+
+    /*
+     * Override of SSLCipherSuite
+     *
+     * We provide two options here:
+     *
+     * o The paranoid and default approach where we force a renegotiation when
+     *   the cipher suite changed in _any_ way (which is straight-forward but
+     *   often forces renegotiations too often and is perhaps not what the
+     *   user actually wanted).
+     *
+     * o The optimized and still secure way where we force a renegotiation
+     *   only if the currently active cipher is no longer contained in the
+     *   reconfigured/new cipher suite. Any other changes are not important
+     *   because it's the servers choice to select a cipher from the ones the
+     *   client supports. So as long as the current cipher is still in the new
+     *   cipher suite we're happy. Because we can assume we would have
+     *   selected it again even when other (better) ciphers exists now in the
+     *   new cipher suite. This approach is fine because the user explicitly
+     *   has to enable this via ``SSLOptions +OptRenegotiate''. So we do no
+     *   implicit optimizations.
+     */
+    if (dc->szCipherSuite || (r->server != handshakeserver)) {
+        /* remember old state */
+
+        if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
+            cipher = SSL_get_current_cipher(ssl);
+        }
+        else {
+            cipher_list_old = (STACK_OF(SSL_CIPHER) *)SSL_get_ciphers(ssl);
+
+            if (cipher_list_old) {
+                cipher_list_old = sk_SSL_CIPHER_dup(cipher_list_old);
+            }
+        }
+
+        /* configure new state */
+        if ((dc->szCipherSuite || sc->server->auth.cipher_suite) &&
+            !SSL_set_cipher_list(ssl, dc->szCipherSuite ?
+                                      dc->szCipherSuite :
+                                      sc->server->auth.cipher_suite)) {
+            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02253)
+                          "Unable to reconfigure (per-directory) "
+                          "permitted SSL ciphers");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+
+            if (cipher_list_old) {
+                sk_SSL_CIPHER_free(cipher_list_old);
+            }
+
+            return HTTP_FORBIDDEN;
+        }
+
+        /* determine whether a renegotiation has to be forced */
+        cipher_list = (STACK_OF(SSL_CIPHER) *)SSL_get_ciphers(ssl);
+
+        if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
+            /* optimized way */
+            if ((!cipher && cipher_list) ||
+                (cipher && !cipher_list))
+            {
+                renegotiate = TRUE;
+            }
+            else if (cipher && cipher_list &&
+                     (sk_SSL_CIPHER_find(cipher_list, cipher) < 0))
+            {
+                renegotiate = TRUE;
+            }
+        }
+        else {
+            /* paranoid way */
+            if ((!cipher_list_old && cipher_list) ||
+                (cipher_list_old && !cipher_list))
+            {
+                renegotiate = TRUE;
+            }
+            else if (cipher_list_old && cipher_list) {
+                for (n = 0;
+                     !renegotiate && (n < sk_SSL_CIPHER_num(cipher_list));
+                     n++)
+                {
+                    SSL_CIPHER *value = sk_SSL_CIPHER_value(cipher_list, n);
+
+                    if (sk_SSL_CIPHER_find(cipher_list_old, value) < 0) {
+                        renegotiate = TRUE;
+                    }
+                }
+
+                for (n = 0;
+                     !renegotiate && (n < sk_SSL_CIPHER_num(cipher_list_old));
+                     n++)
+                {
+                    SSL_CIPHER *value = sk_SSL_CIPHER_value(cipher_list_old, n);
+
+                    if (sk_SSL_CIPHER_find(cipher_list, value) < 0) {
+                        renegotiate = TRUE;
+                    }
+                }
+            }
+        }
+
+        /* cleanup */
+        if (cipher_list_old) {
+            sk_SSL_CIPHER_free(cipher_list_old);
+        }
+
+        if (renegotiate) {
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+            if (sc->cipher_server_pref == TRUE) {
+                SSL_set_options(ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
+            }
+#endif
+            /* tracing */
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02220)
+                         "Reconfigured cipher suite will force renegotiation");
+        }
+    }
+
+    /*
+     * override of SSLVerifyDepth
+     *
+     * The depth checks are handled by us manually inside the verify callback
+     * function and not by OpenSSL internally (and our function is aware of
+     * both the per-server and per-directory contexts). So we cannot ask
+     * OpenSSL about the currently verify depth. Instead we remember it in our
+     * SSLConnRec attached to the SSL* of OpenSSL.  We've to force the
+     * renegotiation if the reconfigured/new verify depth is less than the
+     * currently active/remembered verify depth (because this means more
+     * restriction on the certificate chain).
+     */
+    n = (sslconn->verify_depth != UNSET) ?
+        sslconn->verify_depth :
+        (mySrvConfig(handshakeserver))->server->auth.verify_depth;
+    /* determine the new depth */
+    sslconn->verify_depth = (dc->nVerifyDepth != UNSET) ?
+                            dc->nVerifyDepth : sc->server->auth.verify_depth;
+    if (sslconn->verify_depth < n) {
+        renegotiate = TRUE;
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02254)
+                     "Reduced client verification depth will force "
+                     "renegotiation");
+    }
+
+    /*
+     * override of SSLVerifyClient
+     *
+     * We force a renegotiation if the reconfigured/new verify type is
+     * stronger than the currently active verify type.
+     *
+     * The order is: none << optional_no_ca << optional << require
+     *
+     * Additionally the following optimization is possible here: When the
+     * currently active verify type is "none" but a client certificate is
+     * already known/present, it's enough to manually force a client
+     * verification but at least skip the I/O-intensive renegotiation
+     * handshake.
+     */
+    if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) ||
+        (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
+        /* remember old state */
+        verify_old = SSL_get_verify_mode(ssl);
+        /* configure new state */
+        verify = SSL_VERIFY_NONE;
+
+        if ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+            (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)) {
+            verify |= SSL_VERIFY_PEER_STRICT;
+        }
+
+        if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) ||
+            (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ||
+            (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
+            (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+        {
+            verify |= SSL_VERIFY_PEER;
+        }
+
+        SSL_set_verify(ssl, verify, ssl_callback_SSLVerify);
+        SSL_set_verify_result(ssl, X509_V_OK);
+
+        /* determine whether we've to force a renegotiation */
+        if (!renegotiate && verify != verify_old) {
+            if (((verify_old == SSL_VERIFY_NONE) &&
+                 (verify     != SSL_VERIFY_NONE)) ||
+
+                (!(verify_old & SSL_VERIFY_PEER) &&
+                  (verify     & SSL_VERIFY_PEER)) ||
+
+                (!(verify_old & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) &&
+                  (verify     & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))
+            {
+                renegotiate = TRUE;
+                /* optimization */
+
+                if ((dc->nOptions & SSL_OPT_OPTRENEGOTIATE) &&
+                    (verify_old == SSL_VERIFY_NONE) &&
+                    ((peercert = SSL_get_peer_certificate(ssl)) != NULL))
+                {
+                    renegotiate_quick = TRUE;
+                    X509_free(peercert);
+                }
+
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02255)
+                              "Changed client verification type will force "
+                              "%srenegotiation",
+                              renegotiate_quick ? "quick " : "");
+             }
+        }
+        /* If we're handling a request for a vhost other than the default one,
+         * then we need to make sure that client authentication is properly
+         * enforced. For clients supplying an SNI extension, the peer
+         * certificate verification has happened in the handshake already
+         * (and r->server == handshakeserver). For non-SNI requests,
+         * an additional check is needed here. If client authentication
+         * is configured as mandatory, then we can only proceed if the
+         * CA list doesn't have to be changed (OpenSSL doesn't provide
+         * an option to change the list for an existing session).
+         */
+        if ((r->server != handshakeserver)
+            && renegotiate
+            && ((verify & SSL_VERIFY_PEER) ||
+                (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) {
+            SSLSrvConfigRec *hssc = mySrvConfig(handshakeserver);
+
+#define MODSSL_CFG_CA_NE(f, sc1, sc2) \
+            (sc1->server->auth.f && \
+             (!sc2->server->auth.f || \
+              strNE(sc1->server->auth.f, sc2->server->auth.f)))
+
+            if (MODSSL_CFG_CA_NE(ca_cert_file, sc, hssc) ||
+                MODSSL_CFG_CA_NE(ca_cert_path, sc, hssc)) {
+                if (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02256)
+                         "Non-default virtual host with SSLVerify set to "
+                         "'require' and VirtualHost-specific CA certificate "
+                         "list is only available to clients with TLS server "
+                         "name indication (SNI) support");
+                    SSL_set_verify(ssl, verify_old, NULL);
+                    return HTTP_FORBIDDEN;
+                } else
+                    /* let it pass, possibly with an "incorrect" peer cert,
+                     * so make sure the SSL_CLIENT_VERIFY environment variable
+                     * will indicate partial success only, later on.
+                     */
+                    sslconn->verify_info = "GENEROUS";
+            }
+        }
+    }
+
+    /* If a renegotiation is now required for this location, and the
+     * request includes a message body (and the client has not
+     * requested a "100 Continue" response), then the client will be
+     * streaming the request body over the wire already.  In that
+     * case, it is not possible to stop and perform a new SSL
+     * handshake immediately; once the SSL library moves to the
+     * "accept" state, it will reject the SSL packets which the client
+     * is sending for the request body.
+     *
+     * To allow authentication to complete in this auth hook, the
+     * solution used here is to fill a (bounded) buffer with the
+     * request body, and then to reinject that request body later.
+     */
+    if (renegotiate && !renegotiate_quick
+        && (apr_table_get(r->headers_in, "transfer-encoding")
+            || (apr_table_get(r->headers_in, "content-length")
+                && strcmp(apr_table_get(r->headers_in, "content-length"), "0")))
+        && !r->expecting_100) {
+        int rv;
+        apr_size_t rsize;
+
+        rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE :
+                                                dc->nRenegBufferSize;
+        if (rsize > 0) {
+            /* Fill the I/O buffer with the request body if possible. */
+            rv = ssl_io_buffer_fill(r, rsize);
+        }
+        else {
+            /* If the reneg buffer size is set to zero, just fail. */
+            rv = HTTP_REQUEST_ENTITY_TOO_LARGE;
+        }
+
+        if (rv) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02257)
+                          "could not buffer message body to allow "
+                          "SSL renegotiation to proceed");
+            return rv;
+        }
+    }
+
+    /*
+     * now do the renegotiation if anything was actually reconfigured
+     */
+    if (renegotiate) {
+        /*
+         * Now we force the SSL renegotiation by sending the Hello Request
+         * message to the client. Here we have to do a workaround: Actually
+         * OpenSSL returns immediately after sending the Hello Request (the
+         * intent AFAIK is because the SSL/TLS protocol says it's not a must
+         * that the client replies to a Hello Request). But because we insist
+         * on a reply (anything else is an error for us) we have to go to the
+         * ACCEPT state manually. Using SSL_set_accept_state() doesn't work
+         * here because it resets too much of the connection.  So we set the
+         * state explicitly and continue the handshake manually.
+         */
+        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02221)
+                      "Requesting connection re-negotiation");
+
+        if (renegotiate_quick) {
+            STACK_OF(X509) *cert_stack;
+
+            /* perform just a manual re-verification of the peer */
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02258)
+                         "Performing quick renegotiation: "
+                         "just re-verifying the peer");
+
+            cert_stack = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl);
+
+            cert = SSL_get_peer_certificate(ssl);
+
+            if (!cert_stack && cert) {
+                /* client cert is in the session cache, but there is
+                 * no chain, since ssl3_get_client_certificate()
+                 * sk_X509_shift-ed the peer cert out of the chain.
+                 * we put it back here for the purpose of quick_renegotiation.
+                 */
+                cert_stack = sk_X509_new_null();
+                sk_X509_push(cert_stack, cert);
+            }
+
+            if (!cert_stack || (sk_X509_num(cert_stack) == 0)) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02222)
+                              "Cannot find peer certificate chain");
+
+                return HTTP_FORBIDDEN;
+            }
+
+            if (!(cert_store ||
+                  (cert_store = SSL_CTX_get_cert_store(ctx))))
+            {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02223)
+                              "Cannot find certificate storage");
+
+                return HTTP_FORBIDDEN;
+            }
+
+            if (!cert) {
+                cert = sk_X509_value(cert_stack, 0);
+            }
+
+            X509_STORE_CTX_init(&cert_store_ctx, cert_store, cert, cert_stack);
+            depth = SSL_get_verify_depth(ssl);
+
+            if (depth >= 0) {
+                X509_STORE_CTX_set_depth(&cert_store_ctx, depth);
+            }
+
+            X509_STORE_CTX_set_ex_data(&cert_store_ctx,
+                                       SSL_get_ex_data_X509_STORE_CTX_idx(),
+                                       (char *)ssl);
+
+            if (!X509_verify_cert(&cert_store_ctx)) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02224)
+                              "Re-negotiation verification step failed");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+            }
+
+            SSL_set_verify_result(ssl, cert_store_ctx.error);
+            X509_STORE_CTX_cleanup(&cert_store_ctx);
+
+            if (cert_stack != SSL_get_peer_cert_chain(ssl)) {
+                /* we created this ourselves, so free it */
+                sk_X509_pop_free(cert_stack, X509_free);
+            }
+        }
+        else {
+            const char *reneg_support;
+            request_rec *id = r->main ? r->main : r;
+
+            /* Additional mitigation for CVE-2009-3555: At this point,
+             * before renegotiating, an (entire) request has been read
+             * from the connection.  An attacker may have sent further
+             * data to "prefix" any subsequent request by the victim's
+             * client after the renegotiation; this data may already
+             * have been read and buffered.  Forcing a connection
+             * closure after the response ensures such data will be
+             * discarded.  Legimately pipelined HTTP requests will be
+             * retried anyway with this approach. */
+            if (has_buffered_data(r)) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02259)
+                              "insecure SSL re-negotiation required, but "
+                              "a pipelined request is present; keepalive "
+                              "disabled");
+                r->connection->keepalive = AP_CONN_CLOSE;
+            }
+
+#if defined(SSL_get_secure_renegotiation_support)
+            reneg_support = SSL_get_secure_renegotiation_support(ssl) ?
+                            "client does" : "client does not";
+#else
+            reneg_support = "server does not";
+#endif
+            /* Perform a full renegotiation. */
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02260)
+                          "Performing full renegotiation: complete handshake "
+                          "protocol (%s support secure renegotiation)",
+                          reneg_support);
+
+            SSL_set_session_id_context(ssl,
+                                       (unsigned char *)&id,
+                                       sizeof(id));
+
+            /* Toggle the renegotiation state to allow the new
+             * handshake to proceed. */
+            sslconn->reneg_state = RENEG_ALLOW;
+
+            SSL_renegotiate(ssl);
+            SSL_do_handshake(ssl);
+
+            if (SSL_get_state(ssl) != SSL_ST_OK) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02225)
+                              "Re-negotiation request failed");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+
+                r->connection->keepalive = AP_CONN_CLOSE;
+                return HTTP_FORBIDDEN;
+            }
+
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02226)
+                          "Awaiting re-negotiation handshake");
+
+            /* XXX: Should replace setting state with SSL_renegotiate(ssl);
+             * However, this causes failures in perl-framework currently,
+             * perhaps pre-test if we have already negotiated?
+             */
+#ifdef OPENSSL_NO_SSL_INTERN
+            SSL_set_state(ssl, SSL_ST_ACCEPT);
+#else
+            ssl->state = SSL_ST_ACCEPT;
+#endif
+            SSL_do_handshake(ssl);
+
+            sslconn->reneg_state = RENEG_REJECT;
+
+            if (SSL_get_state(ssl) != SSL_ST_OK) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02261)
+                              "Re-negotiation handshake failed: "
+                              "Not accepted by client!?");
+
+                r->connection->keepalive = AP_CONN_CLOSE;
+                return HTTP_FORBIDDEN;
+            }
+        }
+
+        /*
+         * Remember the peer certificate's DN
+         */
+        if ((cert = SSL_get_peer_certificate(ssl))) {
+            if (sslconn->client_cert) {
+                X509_free(sslconn->client_cert);
+            }
+            sslconn->client_cert = cert;
+            sslconn->client_dn = NULL;
+        }
+
+        /*
+         * Finally check for acceptable renegotiation results
+         */
+        if ((dc->nVerifyClient != SSL_CVERIFY_NONE) ||
+            (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) {
+            BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+                              (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE));
+
+            if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02262)
+                              "Re-negotiation handshake failed: "
+                              "Client verification failed");
+
+                return HTTP_FORBIDDEN;
+            }
+
+            if (do_verify) {
+                if ((peercert = SSL_get_peer_certificate(ssl)) == NULL) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02263)
+                                  "Re-negotiation handshake failed: "
+                                  "Client certificate missing");
+
+                    return HTTP_FORBIDDEN;
+                }
+
+                X509_free(peercert);
+            }
+        }
+
+        /*
+         * Also check that SSLCipherSuite has been enforced as expected.
+         */
+        if (cipher_list) {
+            cipher = SSL_get_current_cipher(ssl);
+            if (sk_SSL_CIPHER_find(cipher_list, cipher) < 0) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02264)
+                             "SSL cipher suite not renegotiated: "
+                             "access to %s denied using cipher %s",
+                              r->filename,
+                              SSL_CIPHER_get_name(cipher));
+                return HTTP_FORBIDDEN;
+            }
+        }
+    }
+
+    /* If we're trying to have the user name set from a client
+     * certificate then we need to set it here. This should be safe as
+     * the user name probably isn't important from an auth checking point
+     * of view as the certificate supplied acts in that capacity.
+     * However, if FakeAuth is being used then this isn't the case so
+     * we need to postpone setting the username until later.
+     */
+    if ((dc->nOptions & SSL_OPT_FAKEBASICAUTH) == 0 && dc->szUserName) {
+        char *val = ssl_var_lookup(r->pool, r->server, r->connection,
+                                   r, (char *)dc->szUserName);
+        if (val && val[0])
+            r->user = val;
+        else
+            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02227)
+                          "Failed to set r->user to '%s'", dc->szUserName);
+    }
+
+    /*
+     * Check SSLRequire boolean expressions
+     */
+    requires = dc->aRequirement;
+    ssl_requires = (ssl_require_t *)requires->elts;
+
+    for (i = 0; i < requires->nelts; i++) {
+        ssl_require_t *req = &ssl_requires[i];
+        const char *errstring;
+        ok = ap_expr_exec(r, req->mpExpr, &errstring);
+
+        if (ok < 0) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02265)
+                          "access to %s failed, reason: Failed to execute "
+                          "SSL requirement expression: %s",
+                          r->filename, errstring);
+
+            /* remember forbidden access for strict require option */
+            apr_table_setn(r->notes, "ssl-access-forbidden", "1");
+
+            return HTTP_FORBIDDEN;
+        }
+
+        if (ok != 1) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02266)
+                          "Access to %s denied for %s "
+                          "(requirement expression not fulfilled)",
+                          r->filename, r->useragent_ip);
+
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02228)
+                          "Failed expression: %s", req->cpExpr);
+
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02229)
+                          "access to %s failed, reason: %s",
+                          r->filename,
+                          "SSL requirement expression not fulfilled");
+
+            /* remember forbidden access for strict require option */
+            apr_table_setn(r->notes, "ssl-access-forbidden", "1");
+
+            return HTTP_FORBIDDEN;
+        }
+    }
+
+    /*
+     * Else access is granted from our point of view (except vendor
+     * handlers override). But we have to return DECLINED here instead
+     * of OK, because mod_auth and other modules still might want to
+     * deny access.
+     */
+
+    return DECLINED;
+}
+
+/*
+ *  Authentication Handler:
+ *  Fake a Basic authentication from the X509 client certificate.
+ *
+ *  This must be run fairly early on to prevent a real authentication from
+ *  occuring, in particular it must be run before anything else that
+ *  authenticates a user.  This means that the Module statement for this
+ *  module should be LAST in the Configuration file.
+ */
+int ssl_hook_UserCheck(request_rec *r)
+{
+    SSLConnRec *sslconn = myConnConfig(r->connection);
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+    SSLDirConfigRec *dc = myDirConfig(r);
+    char *clientdn;
+    const char *auth_line, *username, *password;
+
+    /*
+     * Additionally forbid access (again)
+     * when strict require option is used.
+     */
+    if ((dc->nOptions & SSL_OPT_STRICTREQUIRE) &&
+        (apr_table_get(r->notes, "ssl-access-forbidden")))
+    {
+        return HTTP_FORBIDDEN;
+    }
+
+    /*
+     * We decline when we are in a subrequest.  The Authorization header
+     * would already be present if it was added in the main request.
+     */
+    if (!ap_is_initial_req(r)) {
+        return DECLINED;
+    }
+
+    /*
+     * Make sure the user is not able to fake the client certificate
+     * based authentication by just entering an X.509 Subject DN
+     * ("/XX=YYY/XX=YYY/..") as the username and "password" as the
+     * password.
+     */
+    if ((auth_line = apr_table_get(r->headers_in, "Authorization"))) {
+        if (strcEQ(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+            while ((*auth_line == ' ') || (*auth_line == '\t')) {
+                auth_line++;
+            }
+
+            auth_line = ap_pbase64decode(r->pool, auth_line);
+            username = ap_getword_nulls(r->pool, &auth_line, ':');
+            password = auth_line;
+
+            if ((username[0] == '/') && strEQ(password, "password")) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02035)
+                    "Encountered FakeBasicAuth spoof: %s", username);
+                return HTTP_FORBIDDEN;
+            }
+        }
+    }
+
+    /*
+     * We decline operation in various situations...
+     * - SSLOptions +FakeBasicAuth not configured
+     * - r->user already authenticated
+     * - ssl not enabled
+     * - client did not present a certificate
+     */
+    if (!((sc->enabled == SSL_ENABLED_TRUE || sc->enabled == SSL_ENABLED_OPTIONAL)
+          && sslconn && sslconn->ssl && sslconn->client_cert) ||
+        !(dc->nOptions & SSL_OPT_FAKEBASICAUTH) || r->user)
+    {
+        return DECLINED;
+    }
+
+    if (!sslconn->client_dn) {
+        X509_NAME *name = X509_get_subject_name(sslconn->client_cert);
+        char *cp = X509_NAME_oneline(name, NULL, 0);
+        sslconn->client_dn = apr_pstrdup(r->connection->pool, cp);
+        OPENSSL_free(cp);
+    }
+
+    clientdn = (char *)sslconn->client_dn;
+
+    /*
+     * Fake a password - which one would be immaterial, as, it seems, an empty
+     * password in the users file would match ALL incoming passwords, if only
+     * we were using the standard crypt library routine. Unfortunately, OpenSSL
+     * "fixes" a "bug" in crypt and thus prevents blank passwords from
+     * working.  (IMHO what they really fix is a bug in the users of the code
+     * - failing to program correctly for shadow passwords).  We need,
+     * therefore, to provide a password. This password can be matched by
+     * adding the string "xxj31ZMTZzkVA" as the password in the user file.
+     * This is just the crypted variant of the word "password" ;-)
+     */
+    auth_line = apr_pstrcat(r->pool, "Basic ",
+                            ap_pbase64encode(r->pool,
+                                             apr_pstrcat(r->pool, clientdn,
+                                                         ":password", NULL)),
+                            NULL);
+    apr_table_setn(r->headers_in, "Authorization", auth_line);
+
+    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02036)
+                  "Faking HTTP Basic Auth header: \"Authorization: %s\"",
+                  auth_line);
+
+    return DECLINED;
+}
+
+/* authorization phase */
+int ssl_hook_Auth(request_rec *r)
+{
+    SSLDirConfigRec *dc = myDirConfig(r);
+
+    /*
+     * Additionally forbid access (again)
+     * when strict require option is used.
+     */
+    if ((dc->nOptions & SSL_OPT_STRICTREQUIRE) &&
+        (apr_table_get(r->notes, "ssl-access-forbidden")))
+    {
+        return HTTP_FORBIDDEN;
+    }
+
+    return DECLINED;
+}
+
+/*
+ *   Fixup Handler
+ */
+
+static const char *ssl_hook_Fixup_vars[] = {
+    "SSL_VERSION_INTERFACE",
+    "SSL_VERSION_LIBRARY",
+    "SSL_PROTOCOL",
+    "SSL_SECURE_RENEG",
+    "SSL_COMPRESS_METHOD",
+    "SSL_CIPHER",
+    "SSL_CIPHER_EXPORT",
+    "SSL_CIPHER_USEKEYSIZE",
+    "SSL_CIPHER_ALGKEYSIZE",
+    "SSL_CLIENT_VERIFY",
+    "SSL_CLIENT_M_VERSION",
+    "SSL_CLIENT_M_SERIAL",
+    "SSL_CLIENT_V_START",
+    "SSL_CLIENT_V_END",
+    "SSL_CLIENT_V_REMAIN",
+    "SSL_CLIENT_S_DN",
+    "SSL_CLIENT_I_DN",
+    "SSL_CLIENT_A_KEY",
+    "SSL_CLIENT_A_SIG",
+    "SSL_SERVER_M_VERSION",
+    "SSL_SERVER_M_SERIAL",
+    "SSL_SERVER_V_START",
+    "SSL_SERVER_V_END",
+    "SSL_SERVER_S_DN",
+    "SSL_SERVER_I_DN",
+    "SSL_SERVER_A_KEY",
+    "SSL_SERVER_A_SIG",
+    "SSL_SESSION_ID",
+    "SSL_SESSION_RESUMED",
+#ifdef HAVE_SRP
+    "SSL_SRP_USER",
+    "SSL_SRP_USERINFO",
+#endif
+    NULL
+};
+
+int ssl_hook_Fixup(request_rec *r)
+{
+    SSLConnRec *sslconn = myConnConfig(r->connection);
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+    SSLDirConfigRec *dc = myDirConfig(r);
+    apr_table_t *env = r->subprocess_env;
+    char *var, *val = "";
+#ifdef HAVE_TLSEXT
+    const char *servername;
+#endif
+    STACK_OF(X509) *peer_certs;
+    SSL *ssl;
+    int i;
+
+    /* If "SSLEngine optional" is configured, this is not an SSL
+     * connection, and this isn't a subrequest, send an Upgrade
+     * response header. */
+    if (sc->enabled == SSL_ENABLED_OPTIONAL && !(sslconn && sslconn->ssl)
+        && !r->main) {
+        apr_table_setn(r->headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+        apr_table_mergen(r->headers_out, "Connection", "upgrade");
+    }
+
+    /*
+     * Check to see if SSL is on
+     */
+    if (!(((sc->enabled == SSL_ENABLED_TRUE) || (sc->enabled == SSL_ENABLED_OPTIONAL)) && sslconn && (ssl = sslconn->ssl))) {
+        return DECLINED;
+    }
+
+    /*
+     * Annotate the SSI/CGI environment with standard SSL information
+     */
+    /* the always present HTTPS (=HTTP over SSL) flag! */
+    apr_table_setn(env, "HTTPS", "on");
+
+#ifdef HAVE_TLSEXT
+    /* add content of SNI TLS extension (if supplied with ClientHello) */
+    if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
+        apr_table_set(env, "SSL_TLS_SNI", servername);
+    }
+#endif
+
+    /* standard SSL environment variables */
+    if (dc->nOptions & SSL_OPT_STDENVVARS) {
+        modssl_var_extract_dns(env, sslconn->ssl, r->pool);
+
+        for (i = 0; ssl_hook_Fixup_vars[i]; i++) {
+            var = (char *)ssl_hook_Fixup_vars[i];
+            val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+            if (!strIsEmpty(val)) {
+                apr_table_setn(env, var, val);
+            }
+        }
+    }
+
+    /*
+     * On-demand bloat up the SSI/CGI environment with certificate data
+     */
+    if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
+        val = ssl_var_lookup(r->pool, r->server, r->connection,
+                             r, "SSL_SERVER_CERT");
+
+        apr_table_setn(env, "SSL_SERVER_CERT", val);
+
+        val = ssl_var_lookup(r->pool, r->server, r->connection,
+                             r, "SSL_CLIENT_CERT");
+
+        apr_table_setn(env, "SSL_CLIENT_CERT", val);
+
+        if ((peer_certs = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl))) {
+            for (i = 0; i < sk_X509_num(peer_certs); i++) {
+                var = apr_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i);
+                val = ssl_var_lookup(r->pool, r->server, r->connection,
+                                     r, var);
+                if (val) {
+                    apr_table_setn(env, var, val);
+                }
+            }
+        }
+    }
+
+
+#ifdef SSL_get_secure_renegotiation_support
+    apr_table_setn(r->notes, "ssl-secure-reneg",
+                   SSL_get_secure_renegotiation_support(ssl) ? "1" : "0");
+#endif
+
+    return DECLINED;
+}
+
+/*  _________________________________________________________________
+**
+**  Authz providers for use with mod_authz_core
+**  _________________________________________________________________
+*/
+
+static authz_status ssl_authz_require_ssl_check(request_rec *r,
+                                                const char *require_line,
+                                                const void *parsed)
+{
+    SSLConnRec *sslconn = myConnConfig(r->connection);
+    SSL *ssl = sslconn ? sslconn->ssl : NULL;
+
+    if (ssl)
+        return AUTHZ_GRANTED;
+    else
+        return AUTHZ_DENIED;
+}
+
+static const char *ssl_authz_require_ssl_parse(cmd_parms *cmd,
+                                               const char *require_line,
+                                               const void **parsed)
+{
+    if (require_line && require_line[0])
+        return "'Require ssl' does not take arguments";
+
+    return NULL;
+}
+
+const authz_provider ssl_authz_provider_require_ssl =
+{
+    &ssl_authz_require_ssl_check,
+    &ssl_authz_require_ssl_parse,
+};
+
+static authz_status ssl_authz_verify_client_check(request_rec *r,
+                                                  const char *require_line,
+                                                  const void *parsed)
+{
+    SSLConnRec *sslconn = myConnConfig(r->connection);
+    SSL *ssl = sslconn ? sslconn->ssl : NULL;
+
+    if (!ssl)
+        return AUTHZ_DENIED;
+
+    if (sslconn->verify_error == NULL &&
+        sslconn->verify_info == NULL &&
+        SSL_get_verify_result(ssl) == X509_V_OK)
+    {
+        X509 *xs = SSL_get_peer_certificate(ssl);
+
+        if (xs) {
+            X509_free(xs);
+            return AUTHZ_GRANTED;
+        }
+        else {
+            X509_free(xs);
+        }
+    }
+
+    return AUTHZ_DENIED;
+}
+
+static const char *ssl_authz_verify_client_parse(cmd_parms *cmd,
+                                                 const char *require_line,
+                                                 const void **parsed)
+{
+    if (require_line && require_line[0])
+        return "'Require ssl-verify-client' does not take arguments";
+
+    return NULL;
+}
+
+const authz_provider ssl_authz_provider_verify_client =
+{
+    &ssl_authz_verify_client_check,
+    &ssl_authz_verify_client_parse,
+};
+
+
+
+/*  _________________________________________________________________
+**
+**  OpenSSL Callback Functions
+**  _________________________________________________________________
+*/
+
+/*
+ * Hand out standard DH parameters, based on the authentication strength
+ */
+DH *ssl_callback_TmpDH(SSL *ssl, int export, int keylen)
+{
+    conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+    EVP_PKEY *pkey;
+    int type;
+
+#ifdef SSL_CERT_SET_SERVER
+    /*
+     * When multiple certs/keys are configured for the SSL_CTX: make sure
+     * that we get the private key which is indeed used for the current
+     * SSL connection (available in OpenSSL 1.0.2 or later only)
+     */
+    SSL_set_current_cert(ssl, SSL_CERT_SET_SERVER);
+#endif
+    pkey = SSL_get_privatekey(ssl);
+    type = pkey ? EVP_PKEY_type(pkey->type) : EVP_PKEY_NONE;
+
+    /*
+     * OpenSSL will call us with either keylen == 512 or keylen == 1024
+     * (see the definition of SSL_EXPORT_PKEYLENGTH in ssl_locl.h).
+     * Adjust the DH parameter length according to the size of the
+     * RSA/DSA private key used for the current connection, and always
+     * use at least 1024-bit parameters.
+     * Note: This may cause interoperability issues with implementations
+     * which limit their DH support to 1024 bit - e.g. Java 7 and earlier.
+     * In this case, SSLCertificateFile can be used to specify fixed
+     * 1024-bit DH parameters (with the effect that OpenSSL skips this
+     * callback).
+     */
+    if ((type == EVP_PKEY_RSA) || (type == EVP_PKEY_DSA)) {
+        keylen = EVP_PKEY_bits(pkey);
+    }
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                  "handing out built-in DH parameters for %d-bit authenticated connection", keylen);
+
+    return modssl_get_dh_params(keylen);
+}
+
+/*
+ * This OpenSSL callback function is called when OpenSSL
+ * does client authentication and verifies the certificate chain.
+ */
+int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+{
+    /* Get Apache context back through OpenSSL context */
+    SSL *ssl = X509_STORE_CTX_get_ex_data(ctx,
+                                          SSL_get_ex_data_X509_STORE_CTX_idx());
+    conn_rec *conn      = (conn_rec *)SSL_get_app_data(ssl);
+    request_rec *r      = (request_rec *)SSL_get_app_data2(ssl);
+    server_rec *s       = r ? r->server : mySrvFromConn(conn);
+
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    SSLDirConfigRec *dc = r ? myDirConfig(r) : NULL;
+    SSLConnRec *sslconn = myConnConfig(conn);
+    modssl_ctx_t *mctx  = myCtxConfig(sslconn, sc);
+
+    /* Get verify ingredients */
+    int errnum   = X509_STORE_CTX_get_error(ctx);
+    int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+    int depth, verify;
+
+    /*
+     * Log verification information
+     */
+    ssl_log_cxerror(SSLLOG_MARK, APLOG_DEBUG, 0, conn,
+                    X509_STORE_CTX_get_current_cert(ctx), APLOGNO(02275)
+                    "Certificate Verification, depth %d, "
+                    "CRL checking mode: %s", errdepth,
+                    mctx->crl_check_mode == SSL_CRLCHECK_CHAIN ?
+                    "chain" : (mctx->crl_check_mode == SSL_CRLCHECK_LEAF ?
+                               "leaf" : "none"));
+
+    /*
+     * Check for optionally acceptable non-verifiable issuer situation
+     */
+    if (dc && (dc->nVerifyClient != SSL_CVERIFY_UNSET)) {
+        verify = dc->nVerifyClient;
+    }
+    else {
+        verify = mctx->auth.verify_mode;
+    }
+
+    if (verify == SSL_CVERIFY_NONE) {
+        /*
+         * SSLProxyVerify is either not configured or set to "none".
+         * (this callback doesn't happen in the server context if SSLVerify
+         *  is not configured or set to "none")
+         */
+        return TRUE;
+    }
+
+    if (ssl_verify_error_is_optional(errnum) &&
+        (verify == SSL_CVERIFY_OPTIONAL_NO_CA))
+    {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn, APLOGNO(02037)
+                      "Certificate Verification: Verifiable Issuer is "
+                      "configured as optional, therefore we're accepting "
+                      "the certificate");
+
+        sslconn->verify_info = "GENEROUS";
+        ok = TRUE;
+    }
+
+    /*
+     * Expired certificates vs. "expired" CRLs: by default, OpenSSL
+     * turns X509_V_ERR_CRL_HAS_EXPIRED into a "certificate_expired(45)"
+     * SSL alert, but that's not really the message we should convey to the
+     * peer (at the very least, it's confusing, and in many cases, it's also
+     * inaccurate, as the certificate itself may very well not have expired
+     * yet). We set the X509_STORE_CTX error to something which OpenSSL's
+     * s3_both.c:ssl_verify_alarm_type() maps to SSL_AD_CERTIFICATE_UNKNOWN,
+     * i.e. the peer will receive a "certificate_unknown(46)" alert.
+     * We do not touch errnum, though, so that later on we will still log
+     * the "real" error, as returned by OpenSSL.
+     */
+    if (!ok && errnum == X509_V_ERR_CRL_HAS_EXPIRED) {
+        X509_STORE_CTX_set_error(ctx, -1);
+    }
+
+#ifndef OPENSSL_NO_OCSP
+    /*
+     * Perform OCSP-based revocation checks
+     */
+    if (ok && sc->server->ocsp_enabled) {
+        /* If there was an optional verification error, it's not
+         * possible to perform OCSP validation since the issuer may be
+         * missing/untrusted.  Fail in that case. */
+        if (ssl_verify_error_is_optional(errnum)) {
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
+            errnum = X509_V_ERR_APPLICATION_VERIFICATION;
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, APLOGNO(02038)
+                          "cannot perform OCSP validation for cert "
+                          "if issuer has not been verified "
+                          "(optional_no_ca configured)");
+            ok = FALSE;
+        } else {
+            ok = modssl_verify_ocsp(ctx, sc, s, conn, conn->pool);
+            if (!ok) {
+                errnum = X509_STORE_CTX_get_error(ctx);
+            }
+        }
+    }
+#endif
+
+    /*
+     * If we already know it's not ok, log the real reason
+     */
+    if (!ok) {
+        if (APLOGcinfo(conn)) {
+            ssl_log_cxerror(SSLLOG_MARK, APLOG_INFO, 0, conn,
+                            X509_STORE_CTX_get_current_cert(ctx), APLOGNO(02276)
+                            "Certificate Verification: Error (%d): %s",
+                            errnum, X509_verify_cert_error_string(errnum));
+        } else {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, APLOGNO(02039)
+                          "Certificate Verification: Error (%d): %s",
+                          errnum, X509_verify_cert_error_string(errnum));
+        }
+
+        if (sslconn->client_cert) {
+            X509_free(sslconn->client_cert);
+            sslconn->client_cert = NULL;
+        }
+        sslconn->client_dn = NULL;
+        sslconn->verify_error = X509_verify_cert_error_string(errnum);
+    }
+
+    /*
+     * Finally check the depth of the certificate verification
+     */
+    if (dc && (dc->nVerifyDepth != UNSET)) {
+        depth = dc->nVerifyDepth;
+    }
+    else {
+        depth = mctx->auth.verify_depth;
+    }
+
+    if (errdepth > depth) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, APLOGNO(02040)
+                      "Certificate Verification: Certificate Chain too long "
+                      "(chain has %d certificates, but maximum allowed are "
+                      "only %d)",
+                      errdepth, depth);
+
+        errnum = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+        sslconn->verify_error = X509_verify_cert_error_string(errnum);
+
+        ok = FALSE;
+    }
+
+    /*
+     * And finally signal OpenSSL the (perhaps changed) state
+     */
+    return ok;
+}
+
+#define SSLPROXY_CERT_CB_LOG_FMT \
+   "Proxy client certificate callback: (%s) "
+
+static void modssl_proxy_info_log(conn_rec *c,
+                                  X509_INFO *info,
+                                  const char *msg)
+{
+    ssl_log_cxerror(SSLLOG_MARK, APLOG_DEBUG, 0, c, info->x509, APLOGNO(02277)
+                    SSLPROXY_CERT_CB_LOG_FMT "%s, sending",
+                    (mySrvConfigFromConn(c))->vhost_id, msg);
+}
+
+/*
+ * caller will decrement the cert and key reference
+ * so we need to increment here to prevent them from
+ * being freed.
+ */
+#define modssl_set_cert_info(info, cert, pkey) \
+    *cert = info->x509; \
+    CRYPTO_add(&(*cert)->references, +1, CRYPTO_LOCK_X509); \
+    *pkey = info->x_pkey->dec_pkey; \
+    CRYPTO_add(&(*pkey)->references, +1, CRYPTO_LOCK_X509_PKEY)
+
+int ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+{
+    conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+    server_rec *s = mySrvFromConn(c);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    X509_NAME *ca_name, *issuer, *ca_issuer;
+    X509_INFO *info;
+    X509 *ca_cert;
+    STACK_OF(X509_NAME) *ca_list;
+    STACK_OF(X509_INFO) *certs = sc->proxy->pkp->certs;
+    STACK_OF(X509) *ca_certs;
+    STACK_OF(X509) **ca_cert_chains;
+    int i, j, k;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02267)
+                 SSLPROXY_CERT_CB_LOG_FMT "entered",
+                 sc->vhost_id);
+
+    if (!certs || (sk_X509_INFO_num(certs) <= 0)) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02268)
+                     SSLPROXY_CERT_CB_LOG_FMT
+                     "downstream server wanted client certificate "
+                     "but none are configured", sc->vhost_id);
+        return FALSE;
+    }
+
+    ca_list = SSL_get_client_CA_list(ssl);
+
+    if (!ca_list || (sk_X509_NAME_num(ca_list) <= 0)) {
+        /*
+         * downstream server didn't send us a list of acceptable CA certs,
+         * so we send the first client cert in the list.
+         */
+        info = sk_X509_INFO_value(certs, 0);
+
+        modssl_proxy_info_log(c, info, APLOGNO(02278) "no acceptable CA list");
+
+        modssl_set_cert_info(info, x509, pkey);
+
+        return TRUE;
+    }
+
+    ca_cert_chains = sc->proxy->pkp->ca_certs;
+    for (i = 0; i < sk_X509_NAME_num(ca_list); i++) {
+        ca_name = sk_X509_NAME_value(ca_list, i);
+
+        for (j = 0; j < sk_X509_INFO_num(certs); j++) {
+            info = sk_X509_INFO_value(certs, j);
+            issuer = X509_get_issuer_name(info->x509);
+
+            /* Search certs (by issuer name) one by one*/
+            if (X509_NAME_cmp(issuer, ca_name) == 0) {
+                modssl_proxy_info_log(c, info, APLOGNO(02279)
+                                      "found acceptable cert");
+
+                modssl_set_cert_info(info, x509, pkey);
+
+                return TRUE;
+            }
+
+            if (ca_cert_chains) {
+                /*
+                 * Failed to find direct issuer - search intermediates
+                 * (by issuer name), if provided.
+                 */
+                ca_certs = ca_cert_chains[j];
+                for (k = 0; k < sk_X509_num(ca_certs); k++) {
+                    ca_cert = sk_X509_value(ca_certs, k);
+                    ca_issuer = X509_get_issuer_name(ca_cert);
+
+                    if(X509_NAME_cmp(ca_issuer, ca_name) == 0 ) {
+                        modssl_proxy_info_log(c, info, APLOGNO(02280)
+                                              "found acceptable cert by intermediate CA");
+
+                        modssl_set_cert_info(info, x509, pkey);
+
+                        return TRUE;
+                    }
+                } /* end loop through chained certs */
+            }
+        } /* end loop through available certs */
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02269)
+                 SSLPROXY_CERT_CB_LOG_FMT
+                 "no client certificate found!?", sc->vhost_id);
+
+    return FALSE;
+}
+
+static void ssl_session_log(server_rec *s,
+                            const char *request,
+                            unsigned char *id,
+                            unsigned int idlen,
+                            const char *status,
+                            const char *result,
+                            long timeout)
+{
+    char buf[SSL_SESSION_ID_STRING_LEN];
+    char timeout_str[56] = {'\0'};
+
+    if (!APLOGdebug(s)) {
+        return;
+    }
+
+    if (timeout) {
+        apr_snprintf(timeout_str, sizeof(timeout_str),
+                     "timeout=%lds ", timeout);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
+                 "Inter-Process Session Cache: "
+                 "request=%s status=%s id=%s %s(session %s)",
+                 request, status,
+                 SSL_SESSION_id2sz(id, idlen, buf, sizeof(buf)),
+                 timeout_str, result);
+}
+
+/*
+ *  This callback function is executed by OpenSSL whenever a new SSL_SESSION is
+ *  added to the internal OpenSSL session cache. We use this hook to spread the
+ *  SSL_SESSION also to the inter-process disk-cache to make share it with our
+ *  other Apache pre-forked server processes.
+ */
+int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *session)
+{
+    /* Get Apache context back through OpenSSL context */
+    conn_rec *conn      = (conn_rec *)SSL_get_app_data(ssl);
+    server_rec *s       = mySrvFromConn(conn);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    long timeout        = sc->session_cache_timeout;
+    BOOL rc;
+    unsigned char *id;
+    unsigned int idlen;
+
+    /*
+     * Set the timeout also for the internal OpenSSL cache, because this way
+     * our inter-process cache is consulted only when it's really necessary.
+     */
+    SSL_set_timeout(session, timeout);
+
+    /*
+     * Store the SSL_SESSION in the inter-process cache with the
+     * same expire time, so it expires automatically there, too.
+     */
+#ifdef OPENSSL_NO_SSL_INTERN
+    id = (unsigned char *)SSL_SESSION_get_id(session, &idlen);
+#else
+    id = session->session_id;
+    idlen = session->session_id_length;
+#endif
+
+    rc = ssl_scache_store(s, id, idlen,
+                          apr_time_from_sec(SSL_SESSION_get_time(session)
+                                          + timeout),
+                          session, conn->pool);
+
+    ssl_session_log(s, "SET", id, idlen,
+                    rc == TRUE ? "OK" : "BAD",
+                    "caching", timeout);
+
+    /*
+     * return 0 which means to OpenSSL that the session is still
+     * valid and was not freed by us with SSL_SESSION_free().
+     */
+    return 0;
+}
+
+/*
+ *  This callback function is executed by OpenSSL whenever a
+ *  SSL_SESSION is looked up in the internal OpenSSL cache and it
+ *  was not found. We use this to lookup the SSL_SESSION in the
+ *  inter-process disk-cache where it was perhaps stored by one
+ *  of our other Apache pre-forked server processes.
+ */
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *ssl,
+                                               unsigned char *id,
+                                               int idlen, int *do_copy)
+{
+    /* Get Apache context back through OpenSSL context */
+    conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl);
+    server_rec *s  = mySrvFromConn(conn);
+    SSL_SESSION *session;
+
+    /*
+     * Try to retrieve the SSL_SESSION from the inter-process cache
+     */
+    session = ssl_scache_retrieve(s, id, idlen, conn->pool);
+
+    ssl_session_log(s, "GET", id, idlen,
+                    session ? "FOUND" : "MISSED",
+                    session ? "reuse" : "renewal", 0);
+
+    /*
+     * Return NULL or the retrieved SSL_SESSION. But indicate (by
+     * setting do_copy to 0) that the reference count on the
+     * SSL_SESSION should not be incremented by the SSL library,
+     * because we will no longer hold a reference to it ourself.
+     */
+    *do_copy = 0;
+
+    return session;
+}
+
+/*
+ *  This callback function is executed by OpenSSL whenever a
+ *  SSL_SESSION is removed from the the internal OpenSSL cache.
+ *  We use this to remove the SSL_SESSION in the inter-process
+ *  disk-cache, too.
+ */
+void ssl_callback_DelSessionCacheEntry(SSL_CTX *ctx,
+                                       SSL_SESSION *session)
+{
+    server_rec *s;
+    SSLSrvConfigRec *sc;
+    unsigned char *id;
+    unsigned int idlen;
+
+    /*
+     * Get Apache context back through OpenSSL context
+     */
+    if (!(s = (server_rec *)SSL_CTX_get_app_data(ctx))) {
+        return; /* on server shutdown Apache is already gone */
+    }
+
+    sc = mySrvConfig(s);
+
+    /*
+     * Remove the SSL_SESSION from the inter-process cache
+     */
+#ifdef OPENSSL_NO_SSL_INTERN
+    id = (unsigned char *)SSL_SESSION_get_id(session, &idlen);
+#else
+    id = session->session_id;
+    idlen = session->session_id_length;
+#endif
+
+    /* TODO: Do we need a temp pool here, or are we always shutting down? */
+    ssl_scache_remove(s, id, idlen, sc->mc->pPool);
+
+    ssl_session_log(s, "REM", id, idlen,
+                    "OK", "dead", 0);
+
+    return;
+}
+
+/* Dump debugginfo trace to the log file. */
+static void log_tracing_state(const SSL *ssl, conn_rec *c,
+                              server_rec *s, int where, int rc)
+{
+    /*
+     * create the various trace messages
+     */
+    if (where & SSL_CB_HANDSHAKE_START) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                      "%s: Handshake: start", SSL_LIBRARY_NAME);
+    }
+    else if (where & SSL_CB_HANDSHAKE_DONE) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                      "%s: Handshake: done", SSL_LIBRARY_NAME);
+    }
+    else if (where & SSL_CB_LOOP) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                      "%s: Loop: %s",
+                      SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+    }
+    else if (where & SSL_CB_READ) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                      "%s: Read: %s",
+                      SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+    }
+    else if (where & SSL_CB_WRITE) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                      "%s: Write: %s",
+                      SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+    }
+    else if (where & SSL_CB_ALERT) {
+        char *str = (where & SSL_CB_READ) ? "read" : "write";
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                      "%s: Alert: %s:%s:%s",
+                      SSL_LIBRARY_NAME, str,
+                      SSL_alert_type_string_long(rc),
+                      SSL_alert_desc_string_long(rc));
+    }
+    else if (where & SSL_CB_EXIT) {
+        if (rc == 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                          "%s: Exit: failed in %s",
+                          SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+        }
+        else if (rc < 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
+                          "%s: Exit: error in %s",
+                          SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+        }
+    }
+
+    /*
+     * Because SSL renegotiations can happen at any time (not only after
+     * SSL_accept()), the best way to log the current connection details is
+     * right after a finished handshake.
+     */
+    if (where & SSL_CB_HANDSHAKE_DONE) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02041)
+                      "Protocol: %s, Cipher: %s (%s/%s bits)",
+                      ssl_var_lookup(NULL, s, c, NULL, "SSL_PROTOCOL"),
+                      ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER"),
+                      ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_USEKEYSIZE"),
+                      ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_ALGKEYSIZE"));
+    }
+}
+
+/*
+ * This callback function is executed while OpenSSL processes the SSL
+ * handshake and does SSL record layer stuff.  It's used to trap
+ * client-initiated renegotiations, and for dumping everything to the
+ * log.
+ */
+void ssl_callback_Info(const SSL *ssl, int where, int rc)
+{
+    conn_rec *c;
+    server_rec *s;
+    SSLConnRec *scr;
+
+    /* Retrieve the conn_rec and the associated SSLConnRec. */
+    if ((c = (conn_rec *)SSL_get_app_data((SSL *)ssl)) == NULL) {
+        return;
+    }
+
+    if ((scr = myConnConfig(c)) == NULL) {
+        return;
+    }
+
+    /* If the reneg state is to reject renegotiations, check the SSL
+     * state machine and move to ABORT if a Client Hello is being
+     * read. */
+    if ((where & SSL_CB_ACCEPT_LOOP) && scr->reneg_state == RENEG_REJECT) {
+        int state = SSL_get_state((SSL *)ssl);
+
+        if (state == SSL3_ST_SR_CLNT_HELLO_A
+            || state == SSL23_ST_SR_CLNT_HELLO_A) {
+            scr->reneg_state = RENEG_ABORT;
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02042)
+                          "rejecting client initiated renegotiation");
+        }
+    }
+    /* If the first handshake is complete, change state to reject any
+     * subsequent client-initiated renegotiation. */
+    else if ((where & SSL_CB_HANDSHAKE_DONE) && scr->reneg_state == RENEG_INIT) {
+        scr->reneg_state = RENEG_REJECT;
+    }
+
+    s = mySrvFromConn(c);
+    if (s && APLOGdebug(s)) {
+        log_tracing_state(ssl, c, s, where, rc);
+    }
+}
+
+#ifdef HAVE_TLSEXT
+/*
+ * This callback function is executed when OpenSSL encounters an extended
+ * client hello with a server name indication extension ("SNI", cf. RFC 6066).
+ */
+int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
+{
+    const char *servername =
+                SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+    conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+
+    if (c) {
+        if (servername) {
+            if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
+                                            (void *)servername)) {
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
+                              "SSL virtual host for servername %s found",
+                              servername);
+                return SSL_TLSEXT_ERR_OK;
+            }
+            else {
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
+                              "No matching SSL virtual host for servername "
+                              "%s found (using default/first virtual host)",
+                              servername);
+                /*
+                 * RFC 6066 section 3 says "It is NOT RECOMMENDED to send
+                 * a warning-level unrecognized_name(112) alert, because
+                 * the client's behavior in response to warning-level alerts
+                 * is unpredictable."
+                 *
+                 * To maintain backwards compatibility in mod_ssl, we
+                 * no longer send any alert (neither warning- nor fatal-level),
+                 * i.e. we take the second action suggested in RFC 6066:
+                 * "If the server understood the ClientHello extension but
+                 * does not recognize the server name, the server SHOULD take
+                 * one of two actions: either abort the handshake by sending
+                 * a fatal-level unrecognized_name(112) alert or continue
+                 * the handshake."
+                 */
+            }
+        }
+        else {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02645)
+                          "Server name not provided via TLS extension "
+                          "(using default/first virtual host)");
+        }
+    }
+
+    return SSL_TLSEXT_ERR_NOACK;
+}
+
+/*
+ * Find a (name-based) SSL virtual host where either the ServerName
+ * or one of the ServerAliases matches the supplied name (to be used
+ * with ap_vhost_iterate_given_conn())
+ */
+static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
+{
+    SSLSrvConfigRec *sc;
+    SSL *ssl;
+    BOOL found = FALSE;
+    apr_array_header_t *names;
+    int i;
+    SSLConnRec *sslcon;
+
+    /* check ServerName */
+    if (!strcasecmp(servername, s->server_hostname)) {
+        found = TRUE;
+    }
+
+    /*
+     * if not matched yet, check ServerAlias entries
+     * (adapted from vhost.c:matches_aliases())
+     */
+    if (!found) {
+        names = s->names;
+        if (names) {
+            char **name = (char **)names->elts;
+            for (i = 0; i < names->nelts; ++i) {
+                if (!name[i])
+                    continue;
+                if (!strcasecmp(servername, name[i])) {
+                    found = TRUE;
+                    break;
+                }
+            }
+        }
+    }
+
+    /* if still no match, check ServerAlias entries with wildcards */
+    if (!found) {
+        names = s->wild_names;
+        if (names) {
+            char **name = (char **)names->elts;
+            for (i = 0; i < names->nelts; ++i) {
+                if (!name[i])
+                    continue;
+                if (!ap_strcasecmp_match(servername, name[i])) {
+                    found = TRUE;
+                    break;
+                }
+            }
+        }
+    }
+
+    /* set SSL_CTX (if matched) */
+    sslcon = myConnConfig(c);
+    if (found && (ssl = sslcon->ssl) &&
+        (sc = mySrvConfig(s))) {
+        SSL_CTX *ctx = SSL_set_SSL_CTX(ssl, sc->server->ssl_ctx);
+        /*
+         * SSL_set_SSL_CTX() only deals with the server cert,
+         * so we need to duplicate a few additional settings
+         * from the ctx by hand
+         */
+        SSL_set_options(ssl, SSL_CTX_get_options(ctx));
+        if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
+            (SSL_num_renegotiations(ssl) == 0)) {
+           /*
+            * Only initialize the verification settings from the ctx
+            * if they are not yet set, or if we're called when a new
+            * SSL connection is set up (num_renegotiations == 0).
+            * Otherwise, we would possibly reset a per-directory
+            * configuration which was put into effect by ssl_hook_Access.
+            */
+            SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx),
+                           SSL_CTX_get_verify_callback(ctx));
+        }
+
+        /*
+         * Adjust the session id context. ssl_init_ssl_connection()
+         * always picks the configuration of the first vhost when
+         * calling SSL_new(), but we want to tie the session to the
+         * vhost we have just switched to. Again, we have to make sure
+         * that we're not overwriting a session id context which was
+         * possibly set in ssl_hook_Access(), before triggering
+         * a renegotiation.
+         */
+        if (SSL_num_renegotiations(ssl) == 0) {
+            unsigned char *sid_ctx =
+                (unsigned char *)ap_md5_binary(c->pool,
+                                               (unsigned char *)sc->vhost_id,
+                                               sc->vhost_id_len);
+            SSL_set_session_id_context(ssl, sid_ctx, APR_MD5_DIGESTSIZE*2);
+        }
+
+        /*
+         * Save the found server into our SSLConnRec for later
+         * retrieval
+         */
+        sslcon->server = s;
+
+        /*
+         * There is one special filter callback, which is set
+         * very early depending on the base_server's log level.
+         * If this is not the first vhost we're now selecting
+         * (and the first vhost doesn't use APLOG_TRACE4), then
+         * we need to set that callback here.
+         */
+        if (APLOGtrace4(s)) {
+            BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb);
+            BIO_set_callback_arg(SSL_get_rbio(ssl), (void *)ssl);
+        }
+
+        return 1;
+    }
+
+    return 0;
+}
+#endif /* HAVE_TLSEXT */
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+/*
+ * This callback function is executed when OpenSSL needs a key for encrypting/
+ * decrypting a TLS session ticket (RFC 5077) and a ticket key file has been
+ * configured through SSLSessionTicketKeyFile.
+ */
+int ssl_callback_SessionTicket(SSL *ssl,
+                               unsigned char *keyname,
+                               unsigned char *iv,
+                               EVP_CIPHER_CTX *cipher_ctx,
+                               HMAC_CTX *hctx,
+                               int mode)
+{
+    conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+    server_rec *s = mySrvFromConn(c);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    SSLConnRec *sslconn = myConnConfig(c);
+    modssl_ctx_t *mctx = myCtxConfig(sslconn, sc);
+    modssl_ticket_key_t *ticket_key = mctx->ticket_key;
+
+    if (mode == 1) {
+        /* 
+         * OpenSSL is asking for a key for encrypting a ticket,
+         * see s3_srvr.c:ssl3_send_newsession_ticket()
+         */
+
+        if (ticket_key == NULL) {
+            /* should never happen, but better safe than sorry */
+            return -1;
+        }
+
+        memcpy(keyname, ticket_key->key_name, 16);
+        RAND_pseudo_bytes(iv, EVP_MAX_IV_LENGTH);
+        EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
+                           ticket_key->aes_key, iv);
+        HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL);
+
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02289)
+                      "TLS session ticket key for %s successfully set, "
+                      "creating new session ticket", sc->vhost_id);
+
+        return 0;
+    }
+    else if (mode == 0) {
+        /* 
+         * OpenSSL is asking for the decryption key,
+         * see t1_lib.c:tls_decrypt_ticket()
+         */
+
+        /* check key name */
+        if (ticket_key == NULL || memcmp(keyname, ticket_key->key_name, 16)) {
+            return 0;
+        }
+
+        EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
+                           ticket_key->aes_key, iv);
+        HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL);
+
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02290)
+                      "TLS session ticket key for %s successfully set, "
+                      "decrypting existing session ticket", sc->vhost_id);
+
+        return 1;
+    }
+
+    /* OpenSSL is not expected to call us with modes other than 1 or 0 */
+    return -1;
+}
+#endif /* HAVE_TLS_SESSION_TICKETS */
+
+static int ssl_array_index(apr_array_header_t *array,
+                           const unsigned char *s)
+{
+    int i;
+    for (i = 0; i < array->nelts; i++) {
+        const unsigned char *p = APR_ARRAY_IDX(array, i, const unsigned char*);
+        if (!strcmp((const char *)p, (const char *)s)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+#ifdef HAVE_TLS_ALPN
+/*
+ * Compare to ALPN protocol proposal. Result is similar to strcmp():
+ * 0 gives same precedence, >0 means proto1 is prefered.
+ */
+static int ssl_cmp_alpn_protos(modssl_ctx_t *ctx,
+                                                          const unsigned char *proto1,
+                                                          const unsigned char *proto2)
+{
+       /* TODO: we should have a mod_ssl configuration parameter. */
+    if (ctx && ctx->ssl_alpn_pref) {
+        int index1 = ssl_array_index(ctx->ssl_alpn_pref, proto1);
+        int index2 = ssl_array_index(ctx->ssl_alpn_pref, proto2);
+        if (index2 > index1) {
+            return (index1 >= 0)? 1 : -1;
+        }
+        else if (index1 > index2) {
+            return (index2 >= 0)? -1 : 1;
+        }
+    }
+    /* both have the same index (mabye -1 or no pref configured) and we compare
+     * the names so that spdy3 gets precedence over spdy2. That makes
+     * the outcome at least deterministic. */
+       return strcmp((const char *)proto1, (const char *)proto2);
+}
+
+/*
+ * This callback function is executed when the TLS Application Layer
+ * Protocol Negotiate Extension (ALPN, RFC 7301) is triggered by the client 
+ * hello, giving a list of desired protocol names (in descending preference) 
+ * to the server.
+ * The callback has to select a protocol name or return an error if none of
+ * the clients preferences is supported. 
+ * The selected protocol does not have to be on the client list, according
+ * to RFC 7301, so no checks are performed.
+ * The client protocol list is serialized as length byte followed by ascii
+ * characters (not null-terminated), followed by the next protocol name.
+ */
+int ssl_callback_alpn_select(SSL *ssl,
+                                                        const unsigned char **out, unsigned char *outlen,
+                                                        const unsigned char *in, unsigned int inlen, void *arg)
+{
+       conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
+       SSLConnRec *sslconn = myConnConfig(c);
+    server_rec *s       = mySrvFromConn(c);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    modssl_ctx_t *mctx  = myCtxConfig(sslconn, sc);
+       const unsigned char *alpn_http1 = (const unsigned char*)"http/1.1";
+       apr_array_header_t *client_protos;
+       apr_array_header_t *proposed_protos;
+       int i;
+
+       /* If the connection object is not available,
+        * then there's nothing for us to do. */
+       if (c == NULL) {
+               return SSL_TLSEXT_ERR_OK;
+       }
+       
+       if (inlen == 0) {
+               // someone tries to trick us?
+               ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02306)
+                                         "alpn client protocol list empty");
+               return SSL_TLSEXT_ERR_ALERT_FATAL;
+       }
+       
+       client_protos = apr_array_make(c->pool, 0, sizeof(char *));
+       for (i = 0; i < inlen; /**/) {
+               unsigned int plen = in[i++];
+               if (plen + i > inlen) {
+                       // someone tries to trick us?
+                       ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02306)
+                                                 "alpn protocol identier too long");
+                       return SSL_TLSEXT_ERR_ALERT_FATAL;
+               }
+               APR_ARRAY_PUSH(client_protos, char*) =
+                       apr_pstrndup(c->pool, (const char *)in+i, plen);
+               i += plen;
+       }
+       
+       /* Regardless of installed hooks, the http/1.1 protocol is always
+        * supported by us. Add it to the proposals if the client also
+        * offers it. */
+       proposed_protos = apr_array_make(c->pool, client_protos->nelts+1,
+                                                                        sizeof(char *));
+       if (ssl_array_index(client_protos, alpn_http1) >= 0) {
+               APR_ARRAY_PUSH(proposed_protos, const unsigned char*) = alpn_http1;
+       }
+       
+       if (sslconn->alpn_proposefns != NULL) {
+               /* Invoke our alpn_propos_proto hooks, giving other modules a chance to
+                * propose protocol names for selection. We might have several such
+                * hooks installed and if two make a proposal, we need to give 
+                * preference to one.
+                */
+               for (i = 0; i < sslconn->alpn_proposefns->nelts; i++) {
+                       ssl_alpn_propose_protos fn =
+                               APR_ARRAY_IDX(sslconn->alpn_proposefns, i,
+                                                         ssl_alpn_propose_protos);
+                       
+                       if (fn(c, client_protos, proposed_protos) == DONE)
+                               break;
+               }
+       }
+
+       if (proposed_protos->nelts <= 0) {
+               ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02306)
+                                         "none of the client alpn protocols are supported");
+               return SSL_TLSEXT_ERR_ALERT_FATAL;
+       }
+       
+       /* Now select the most preferred protocol from the proposals. */
+       *out = APR_ARRAY_IDX(proposed_protos, 0, const unsigned char *);
+       for (i = 1; i < proposed_protos->nelts; ++i) {
+               const unsigned char *proto = APR_ARRAY_IDX(proposed_protos, i,
+                                                                                                  const unsigned char*);
+               /* Do we prefer it over existing candidate? */
+               if (ssl_cmp_alpn_protos(mctx, *out, proto) < 0) {
+                       *out = proto;
+               }
+       }
+       
+       size_t len = strlen((const char*)*out);
+       if (len > 255) {
+               ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02306)
+                                         "alpn negotiated protocol name too long");
+               return SSL_TLSEXT_ERR_ALERT_FATAL;
+       }
+       *outlen = (unsigned char)len;
+
+       return SSL_TLSEXT_ERR_OK;
+}
+
+#elif defined(HAVE_TLS_NPN)
+/*
+ * This callback function is executed when SSL needs to decide what protocols
+ * to advertise during Next Protocol Negotiation (NPN).  It must produce a
+ * string in wire format -- a sequence of length-prefixed strings -- indicating
+ * the advertised protocols.  Refer to SSL_CTX_set_next_protos_advertised_cb
+ * in OpenSSL for reference.
+ */
+int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data_out,
+                                     unsigned int *size_out, void *arg)
+{
+    conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
+    SSLConnRec *sslconn = myConnConfig(c);
+    server_rec *s       = mySrvFromConn(c);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    modssl_ctx_t *mctx  = myCtxConfig(sslconn, sc);
+    apr_array_header_t *protos;
+    int num_protos;
+    unsigned int size;
+    int i, j;
+    unsigned char *data;
+    unsigned char *start;
+
+    *data_out = NULL;
+    *size_out = 0;
+
+    ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02306)
+                  "advertisingNextProtos");
+
+    /* If the connection object is not available, or there are no NPN
+     * hooks registered, then there's nothing for us to do. */
+    if (c == NULL || sslconn->alpn_proposefns == NULL) {
+        return SSL_TLSEXT_ERR_OK;
+    }
+
+    /* Invoke our alpn_propose_proto hook, giving other modules a chance to
+     * add alternate protocol names to advertise. */
+    protos = apr_array_make(c->pool, 0, sizeof(char *));
+    for (i = 0; i < sslconn->alpn_proposefns->nelts; i++) {
+        ssl_alpn_propose_protos fn =
+            APR_ARRAY_IDX(sslconn->alpn_proposefns, i, ssl_alpn_propose_protos);
+        
+        if (fn(c, NULL, protos) == DONE)
+            break;
+    }
+    num_protos = protos->nelts;
+
+    ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02306)
+                  "alpn protos %d to advertise, %d in pref config", num_protos, mctx->ssl_alpn_pref->nelts );
+       if (num_protos > 1 && mctx->ssl_alpn_pref && mctx->ssl_alpn_pref->nelts > 0) {
+               /* Sort the protocol names according to our configured preferences. */
+               int insert_idx = 0;
+               for (i = 0; i < mctx->ssl_alpn_pref->nelts; ++i) {
+                       const char *proto = APR_ARRAY_IDX(mctx->ssl_alpn_pref, i, const char*);
+                       int idx = ssl_array_index(protos, proto);
+                       if (idx > insert_idx) {
+                               /* bubble found protocol up */
+                for (j = idx; j > insert_idx; --j) {
+                    ((const char **)protos->elts)[j] = ((const char **)protos->elts)[j-1];
+                }
+                ((const char **)protos->elts)[insert_idx] = proto;
+                               ++insert_idx;
+                       }
+               }
+       }
+    
+    /* We now have a list of null-terminated strings; we need to concatenate
+     * them together into a single string, where each protocol name is prefixed
+     * by its length.  First, calculate how long that string will be. */
+    size = 0;
+    for (i = 0; i < num_protos; ++i) {
+        const char *string = APR_ARRAY_IDX(protos, i, const char*);
+        unsigned int length = strlen(string);
+        /* If the protocol name is too long (the length must fit in one byte),
+         * then log an error and skip it. */
+        if (length > 255) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02307)
+                          "SSL NPN protocol name too long (length=%u): %s",
+                          length, string);
+            continue;
+        }
+        /* Leave room for the length prefix (one byte) plus the protocol name
+         * itself. */
+        size += 1 + length;
+    }
+
+    /* If there is nothing to advertise (either because no modules added
+     * anything to the protos array, or because all strings added to the array
+     * were skipped), then we're done. */
+    if (size == 0) {
+        return SSL_TLSEXT_ERR_OK;
+    }
+
+    /* Now we can build the string.  Copy each protocol name string into the
+     * larger string, prefixed by its length. */
+    data = apr_palloc(c->pool, size * sizeof(unsigned char));
+    start = data;
+    for (i = 0; i < num_protos; ++i) {
+        const char *string = APR_ARRAY_IDX(protos, i, const char*);
+        apr_size_t length = strlen(string);
+        if (length > 255)
+            continue;
+        *start = (unsigned char)length;
+        ++start;
+        memcpy(start, string, length * sizeof(unsigned char));
+        start += length;
+    }
+
+    /* Success. */
+    *data_out = data;
+    *size_out = size;
+    return SSL_TLSEXT_ERR_OK;
+}
+
+#endif /* HAVE_TLS_NPN */
+
+#ifdef HAVE_SRP
+
+int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg)
+{
+    modssl_ctx_t *mctx = (modssl_ctx_t *)arg;
+    char *username = SSL_get_srp_username(ssl);
+    SRP_user_pwd *u;
+
+    if (username == NULL
+        || (u = SRP_VBASE_get_by_user(mctx->srp_vbase, username)) == NULL) {
+        *ad = SSL_AD_UNKNOWN_PSK_IDENTITY;
+        return SSL3_AL_FATAL;
+    }
+
+    if (SSL_set_srp_server_param(ssl, u->N, u->g, u->s, u->v, u->info) < 0) {
+        *ad = SSL_AD_INTERNAL_ERROR;
+        return SSL3_AL_FATAL;
+    }
+
+    /* reset all other options */
+    SSL_set_verify(ssl, SSL_VERIFY_NONE,  ssl_callback_SSLVerify);
+    return SSL_ERROR_NONE;
+}
+
+#endif /* HAVE_SRP */
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_log.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_log.c
new file mode 100644 (file)
index 0000000..2c87638
--- /dev/null
@@ -0,0 +1,238 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_log.c
+ *  Logging Facility
+ */
+                             /* ``The difference between a computer
+                                  industry job and open-source software
+                                  hacking is about 30 hours a week.''
+                                         -- Ralf S. Engelschall     */
+#include "ssl_private.h"
+
+/*  _________________________________________________________________
+**
+**  Logfile Support
+**  _________________________________________________________________
+*/
+
+static const struct {
+    const char *cpPattern;
+    const char *cpAnnotation;
+} ssl_log_annotate[] = {
+    { "*envelope*bad*decrypt*", "wrong pass phrase!?" },
+    { "*CLIENT_HELLO*unknown*protocol*", "speaking not SSL to HTTPS port!?" },
+    { "*CLIENT_HELLO*http*request*", "speaking HTTP to HTTPS port!?" },
+    { "*SSL3_READ_BYTES:sslv3*alert*bad*certificate*", "Subject CN in certificate not server name or identical to CA!?" },
+    { "*self signed certificate in certificate chain*", "Client certificate signed by CA not known to server?" },
+    { "*peer did not return a certificate*", "No CAs known to server for verification?" },
+    { "*no shared cipher*", "Too restrictive SSLCipherSuite or using DSA server certificate?" },
+    { "*no start line*", "Bad file contents or format - or even just a forgotten SSLCertificateKeyFile?" },
+    { "*bad password read*", "You entered an incorrect pass phrase!?" },
+    { "*bad mac decode*", "Browser still remembered details of a re-created server certificate?" },
+    { NULL, NULL }
+};
+
+static const char *ssl_log_annotation(const char *error)
+{
+    int i = 0;
+
+    while (ssl_log_annotate[i].cpPattern != NULL
+           && ap_strcmp_match(error, ssl_log_annotate[i].cpPattern) != 0)
+        i++;
+
+    return ssl_log_annotate[i].cpAnnotation;
+}
+
+apr_status_t ssl_die(server_rec *s)
+{
+    if (s != NULL && s->is_virtual && s->error_fname != NULL)
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, NULL, APLOGNO(02311)
+                     "Fatal error initialising mod_ssl, exiting. "
+                     "See %s for more information",
+                     ap_server_root_relative(s->process->pool,
+                                             s->error_fname));
+    else
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, NULL, APLOGNO(02312)
+                     "Fatal error initialising mod_ssl, exiting.");
+
+    return APR_EGENERAL;
+}
+
+/*
+ * Prints the SSL library error information.
+ */
+void ssl_log_ssl_error(const char *file, int line, int level, server_rec *s)
+{
+    unsigned long e;
+    const char *data;
+    int flags;
+
+    while ((e = ERR_peek_error_line_data(NULL, NULL, &data, &flags))) {
+        const char *annotation;
+        char err[256];
+
+        if (!(flags & ERR_TXT_STRING)) {
+            data = NULL;
+        }
+
+        ERR_error_string_n(e, err, sizeof err);
+        annotation = ssl_log_annotation(err);
+
+        ap_log_error(file, line, APLOG_MODULE_INDEX, level, 0, s,
+                     "SSL Library Error: %s%s%s%s%s%s",
+                     /* %s */
+                     err,
+                     /* %s%s%s */
+                     data ? " (" : "", data ? data : "", data ? ")" : "",
+                     /* %s%s */
+                     annotation ? " -- " : "",
+                     annotation ? annotation : "");
+
+        /* Pop the error off the stack: */
+        ERR_get_error();
+    }
+}
+
+static void ssl_log_cert_error(const char *file, int line, int level,
+                               apr_status_t rv, const server_rec *s,
+                               const conn_rec *c, const request_rec *r,
+                               apr_pool_t *p, X509 *cert, const char *format,
+                               va_list ap)
+{
+    char buf[HUGE_STRING_LEN];
+    int msglen, n;
+    char *name;
+
+    apr_vsnprintf(buf, sizeof buf, format, ap);
+
+    msglen = strlen(buf);
+
+    if (cert) {
+        BIO *bio = BIO_new(BIO_s_mem());
+
+        if (bio) {
+            /*
+             * Limit the maximum length of the subject and issuer DN strings
+             * in the log message. 300 characters should always be sufficient
+             * for holding both the timestamp, module name, pid etc. stuff
+             * at the beginning of the line and the trailing information about
+             * serial, notbefore and notafter.
+             */
+            int maxdnlen = (HUGE_STRING_LEN - msglen - 300) / 2;
+
+            BIO_puts(bio, " [subject: ");
+            name = SSL_X509_NAME_to_string(p, X509_get_subject_name(cert),
+                                           maxdnlen);
+            if (!strIsEmpty(name)) {
+                BIO_puts(bio, name);
+            } else {
+                BIO_puts(bio, "-empty-");
+            }
+
+            BIO_puts(bio, " / issuer: ");
+            name = SSL_X509_NAME_to_string(p, X509_get_issuer_name(cert),
+                                           maxdnlen);
+            if (!strIsEmpty(name)) {
+                BIO_puts(bio, name);
+            } else {
+                BIO_puts(bio, "-empty-");
+            }
+
+            BIO_puts(bio, " / serial: ");
+            if (i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert)) == -1)
+                BIO_puts(bio, "(ERROR)");
+
+            BIO_puts(bio, " / notbefore: ");
+            ASN1_TIME_print(bio, X509_get_notBefore(cert));
+
+            BIO_puts(bio, " / notafter: ");
+            ASN1_TIME_print(bio, X509_get_notAfter(cert));
+
+            BIO_puts(bio, "]");
+
+            n = BIO_read(bio, buf + msglen, sizeof buf - msglen - 1);
+            if (n > 0)
+               buf[msglen + n] = '\0';
+
+            BIO_free(bio);
+        }
+    }
+    else {
+        apr_snprintf(buf + msglen, sizeof buf - msglen,
+                     " [certificate: -not available-]");
+    }
+
+    if (r) {
+        ap_log_rerror(file, line, APLOG_MODULE_INDEX, level, rv, r, "%s", buf);
+    }
+    else if (c) {
+        ap_log_cerror(file, line, APLOG_MODULE_INDEX, level, rv, c, "%s", buf);
+    }
+    else if (s) {
+        ap_log_error(file, line, APLOG_MODULE_INDEX, level, rv, s, "%s", buf);
+    }
+
+}
+
+/*
+ * Wrappers for ap_log_error/ap_log_cerror/ap_log_rerror which log additional
+ * details of the X509 cert. For ssl_log_xerror, a pool needs to be passed in
+ * as well (for temporary allocation of the cert's subject/issuer name strings,
+ * in the other cases we use the connection and request pool, respectively).
+ */
+void ssl_log_xerror(const char *file, int line, int level, apr_status_t rv,
+                    apr_pool_t *ptemp, server_rec *s, X509 *cert,
+                    const char *fmt, ...)
+{
+    if (APLOG_IS_LEVEL(s,level)) {
+       va_list ap;
+       va_start(ap, fmt);
+       ssl_log_cert_error(file, line, level, rv, s, NULL, NULL, ptemp,
+                          cert, fmt, ap);
+       va_end(ap);
+    }
+}
+
+void ssl_log_cxerror(const char *file, int line, int level, apr_status_t rv,
+                     conn_rec *c, X509 *cert, const char *fmt, ...)
+{
+    if (APLOG_IS_LEVEL(mySrvFromConn(c),level)) {
+       va_list ap;
+       va_start(ap, fmt);
+       ssl_log_cert_error(file, line, level, rv, NULL, c, NULL, c->pool,
+                          cert, fmt, ap);
+       va_end(ap);
+    }
+}
+
+void ssl_log_rxerror(const char *file, int line, int level, apr_status_t rv,
+                     request_rec *r, X509 *cert, const char *fmt, ...)
+{
+    if (APLOG_R_IS_LEVEL(r,level)) {
+       va_list ap;
+       va_start(ap, fmt);
+       ssl_log_cert_error(file, line, level, rv, NULL, NULL, r, r->pool,
+                          cert, fmt, ap);
+       va_end(ap);
+    }
+}
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_mutex.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_mutex.c
new file mode 100644 (file)
index 0000000..e915a16
--- /dev/null
@@ -0,0 +1,111 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_mutex.c
+ *  Semaphore for Mutual Exclusion
+ */
+                             /* ``Real programmers confuse
+                                  Christmas and Halloween
+                                  because DEC 25 = OCT 31.''
+                                             -- Unknown     */
+
+#include "ssl_private.h"
+
+int ssl_mutex_init(server_rec *s, apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+
+    /* A mutex is only needed if a session cache is configured, and
+     * the provider used is not internally multi-process/thread
+     * safe. */
+    if (!mc->sesscache
+        || (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) == 0) {
+        return TRUE;
+    }
+
+    if (mc->pMutex) {
+        return TRUE;
+    }
+
+    if ((rv = ap_global_mutex_create(&mc->pMutex, NULL, SSL_CACHE_MUTEX_TYPE,
+                                     NULL, s, s->process->pool, 0))
+            != APR_SUCCESS) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+int ssl_mutex_reinit(server_rec *s, apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+    const char *lockfile;
+
+    if (mc->pMutex == NULL || !mc->sesscache
+        || (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) == 0) {
+        return TRUE;
+    }
+
+    lockfile = apr_global_mutex_lockfile(mc->pMutex);
+    if ((rv = apr_global_mutex_child_init(&mc->pMutex,
+                                          lockfile,
+                                          p)) != APR_SUCCESS) {
+        if (lockfile)
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02024)
+                         "Cannot reinit %s mutex with file `%s'",
+                         SSL_CACHE_MUTEX_TYPE, lockfile);
+        else
+            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(02025)
+                         "Cannot reinit %s mutex", SSL_CACHE_MUTEX_TYPE);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+int ssl_mutex_on(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+
+    if ((rv = apr_global_mutex_lock(mc->pMutex)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(02026)
+                     "Failed to acquire SSL session cache lock");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+int ssl_mutex_off(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+
+    if ((rv = apr_global_mutex_unlock(mc->pMutex)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(02027)
+                     "Failed to release SSL session cache lock");
+        return FALSE;
+    }
+    return TRUE;
+}
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_ocsp.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_ocsp.c
new file mode 100644 (file)
index 0000000..0ac223a
--- /dev/null
@@ -0,0 +1,300 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ssl_private.h"
+
+#ifndef OPENSSL_NO_OCSP
+#include "apr_base64.h"
+
+/* Return the responder URI specified in the given certificate, or
+ * NULL if none specified. */
+static const char *extract_responder_uri(X509 *cert, apr_pool_t *pool)
+{
+    STACK_OF(ACCESS_DESCRIPTION) *values;
+    char *result = NULL;
+    int j;
+
+    values = X509_get_ext_d2i(cert, NID_info_access, NULL, NULL);
+    if (!values) {
+        return NULL;
+    }
+
+    for (j = 0; j < sk_ACCESS_DESCRIPTION_num(values) && !result; j++) {
+        ACCESS_DESCRIPTION *value = sk_ACCESS_DESCRIPTION_value(values, j);
+
+        /* Name found in extension, and is a URI: */
+        if (OBJ_obj2nid(value->method) == NID_ad_OCSP
+            && value->location->type == GEN_URI) {
+            result = apr_pstrdup(pool,
+                                 (char *)value->location->d.uniformResourceIdentifier->data);
+        }
+    }
+
+    AUTHORITY_INFO_ACCESS_free(values);
+
+    return result;
+}
+
+/* Return the responder URI object which should be used in the given
+ * configuration for the given certificate, or NULL if none can be
+ * determined. */
+static apr_uri_t *determine_responder_uri(SSLSrvConfigRec *sc, X509 *cert,
+                                          conn_rec *c, apr_pool_t *p)
+{
+    apr_uri_t *u = apr_palloc(p, sizeof *u);
+    const char *s;
+    apr_status_t rv;
+
+    /* Use default responder URL if forced by configuration, else use
+     * certificate-specified responder, falling back to default if
+     * necessary and possible. */
+    if (sc->server->ocsp_force_default) {
+        s = sc->server->ocsp_responder;
+    }
+    else {
+        s = extract_responder_uri(cert, p);
+
+        if (s == NULL && sc->server->ocsp_responder) {
+            s = sc->server->ocsp_responder;
+        }
+    }
+
+    if (s == NULL) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01918)
+                      "no OCSP responder specified in certificate and "
+                      "no default configured");
+        return NULL;
+    }
+
+    rv = apr_uri_parse(p, s, u);
+    if (rv || !u->hostname) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(01919)
+                      "failed to parse OCSP responder URI '%s'", s);
+        return NULL;
+    }
+
+    if (strcasecmp(u->scheme, "http") != 0) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(01920)
+                      "cannot handle OCSP responder URI '%s'", s);
+        return NULL;
+    }
+
+    if (!u->port) {
+        u->port = apr_uri_port_of_scheme(u->scheme);
+    }
+
+    return u;
+}
+
+/* Create an OCSP request for the given certificate; returning the
+ * certificate ID in *certid and *issuer on success.  Returns the
+ * request object on success, or NULL on error. */
+static OCSP_REQUEST *create_request(X509_STORE_CTX *ctx, X509 *cert,
+                                    OCSP_CERTID **certid,
+                                    server_rec *s, apr_pool_t *p,
+                                    SSLSrvConfigRec *sc)
+{
+    OCSP_REQUEST *req = OCSP_REQUEST_new();
+
+    *certid = OCSP_cert_to_id(NULL, cert, ctx->current_issuer);
+    if (!*certid || !OCSP_request_add0_id(req, *certid)) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01921)
+                     "could not retrieve certificate id");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+        return NULL;
+    }
+
+    if (sc->server->ocsp_use_request_nonce != FALSE) {
+        OCSP_request_add1_nonce(req, 0, -1);
+    }
+
+    return req;
+}
+
+/* Verify the OCSP status of given certificate.  Returns
+ * V_OCSP_CERTSTATUS_* result code. */
+static int verify_ocsp_status(X509 *cert, X509_STORE_CTX *ctx, conn_rec *c,
+                              SSLSrvConfigRec *sc, server_rec *s,
+                              apr_pool_t *pool)
+{
+    int rc = V_OCSP_CERTSTATUS_GOOD;
+    OCSP_RESPONSE *response = NULL;
+    OCSP_BASICRESP *basicResponse = NULL;
+    OCSP_REQUEST *request = NULL;
+    OCSP_CERTID *certID = NULL;
+    apr_uri_t *ruri;
+
+    ruri = determine_responder_uri(sc, cert, c, pool);
+    if (!ruri) {
+        return V_OCSP_CERTSTATUS_UNKNOWN;
+    }
+
+    request = create_request(ctx, cert, &certID, s, pool, sc);
+    if (request) {
+        apr_interval_time_t to = sc->server->ocsp_responder_timeout == UNSET ?
+                                 apr_time_from_sec(DEFAULT_OCSP_TIMEOUT) :
+                                 sc->server->ocsp_responder_timeout;
+        response = modssl_dispatch_ocsp_request(ruri, to, request, c, pool);
+    }
+
+    if (!request || !response) {
+        rc = V_OCSP_CERTSTATUS_UNKNOWN;
+    }
+
+    if (rc == V_OCSP_CERTSTATUS_GOOD) {
+        int r = OCSP_response_status(response);
+
+        if (r != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01922)
+                         "OCSP response not successful: %d", rc);
+            rc = V_OCSP_CERTSTATUS_UNKNOWN;
+        }
+    }
+
+    if (rc == V_OCSP_CERTSTATUS_GOOD) {
+        basicResponse = OCSP_response_get1_basic(response);
+        if (!basicResponse) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01923)
+                          "could not retrieve OCSP basic response");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+            rc = V_OCSP_CERTSTATUS_UNKNOWN;
+        }
+    }
+
+    if (rc == V_OCSP_CERTSTATUS_GOOD &&
+            sc->server->ocsp_use_request_nonce != FALSE &&
+            OCSP_check_nonce(request, basicResponse) != 1) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01924)
+                    "Bad OCSP responder answer (bad nonce)");
+        rc = V_OCSP_CERTSTATUS_UNKNOWN;
+    }
+
+    if (rc == V_OCSP_CERTSTATUS_GOOD) {
+        /* TODO: allow flags configuration. */
+        if (OCSP_basic_verify(basicResponse, NULL, ctx->ctx, 0) != 1) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01925)
+                        "failed to verify the OCSP response");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+            rc = V_OCSP_CERTSTATUS_UNKNOWN;
+        }
+    }
+
+    if (rc == V_OCSP_CERTSTATUS_GOOD) {
+        int reason = -1, status;
+        ASN1_GENERALIZEDTIME *thisup = NULL, *nextup = NULL;
+
+        rc = OCSP_resp_find_status(basicResponse, certID, &status,
+                                   &reason, NULL, &thisup, &nextup);
+        if (rc != 1) {
+            ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02272)
+                            "failed to retrieve OCSP response status");
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+            rc = V_OCSP_CERTSTATUS_UNKNOWN;
+        }
+        else {
+            rc = status;
+        }
+
+        /* Check whether the response is inside the defined validity
+         * period; otherwise fail.  */
+        if (rc != V_OCSP_CERTSTATUS_UNKNOWN) {
+            long resptime_skew = sc->server->ocsp_resptime_skew == UNSET ?
+                                 DEFAULT_OCSP_MAX_SKEW : sc->server->ocsp_resptime_skew;
+            /* oscp_resp_maxage can be passed verbatim - UNSET (-1) means
+             * that responses can be of any age as long as nextup is in the
+             * future. */
+            int vrc  = OCSP_check_validity(thisup, nextup, resptime_skew,
+                                           sc->server->ocsp_resp_maxage);
+            if (vrc != 1) {
+                ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02273)
+                                "OCSP response outside validity period");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+                rc = V_OCSP_CERTSTATUS_UNKNOWN;
+            }
+        }
+
+        {
+            int level =
+                (status == V_OCSP_CERTSTATUS_GOOD) ? APLOG_INFO : APLOG_ERR;
+            const char *result =
+                status == V_OCSP_CERTSTATUS_GOOD ? "good" :
+                (status == V_OCSP_CERTSTATUS_REVOKED ? "revoked" : "unknown");
+
+            ssl_log_cxerror(SSLLOG_MARK, level, 0, c, cert,
+                            "OCSP validation completed, "
+                            "certificate status: %s (%d, %d)",
+                            result, status, reason);
+        }
+    }
+
+    if (request) OCSP_REQUEST_free(request);
+    if (response) OCSP_RESPONSE_free(response);
+    if (basicResponse) OCSP_BASICRESP_free(basicResponse);
+    /* certID is freed when the request is freed */
+
+    return rc;
+}
+
+int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc,
+                       server_rec *s, conn_rec *c, apr_pool_t *pool)
+{
+    X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+    apr_pool_t *vpool;
+    int rv;
+
+    if (!cert) {
+        /* starting with OpenSSL 1.0, X509_STORE_CTX_get_current_cert()
+         * may yield NULL. Return early, but leave the ctx error as is. */
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                      "No cert available to check with OCSP");
+        return 1;
+    }
+    else if (cert->valid && X509_check_issued(cert,cert) == X509_V_OK) {
+        /* don't do OCSP checking for valid self-issued certs */
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                      "Skipping OCSP check for valid self-issued cert");
+        X509_STORE_CTX_set_error(ctx, X509_V_OK);
+        return 1;
+    }
+
+    /* Create a temporary pool to constrain memory use (the passed-in
+     * pool may be e.g. a connection pool). */
+    apr_pool_create(&vpool, pool);
+
+    rv = verify_ocsp_status(cert, ctx, c, sc, s, vpool);
+
+    apr_pool_destroy(vpool);
+
+    /* Propagate the verification status back to the passed-in
+     * context. */
+    switch (rv) {
+    case V_OCSP_CERTSTATUS_GOOD:
+        X509_STORE_CTX_set_error(ctx, X509_V_OK);
+        break;
+
+    case V_OCSP_CERTSTATUS_REVOKED:
+        X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+        break;
+
+    case V_OCSP_CERTSTATUS_UNKNOWN:
+        /* correct error code for application errors? */
+        X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
+        break;
+    }
+
+    return rv == V_OCSP_CERTSTATUS_GOOD;
+}
+#endif /* HAVE_OCSP */
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_pphrase.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_pphrase.c
new file mode 100644 (file)
index 0000000..a1f8734
--- /dev/null
@@ -0,0 +1,623 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_pphrase.c
+ *  Pass Phrase Dialog
+ */
+                             /* ``Treat your password like your
+                                  toothbrush. Don't let anybody
+                                  else use it, and get a new one
+                                  every six months.''
+                                           -- Clifford Stoll     */
+#include "ssl_private.h"
+
+typedef struct {
+    server_rec         *s;
+    apr_pool_t         *p;
+    apr_array_header_t *aPassPhrase;
+    int                 nPassPhraseCur;
+    char               *cpPassPhraseCur;
+    int                 nPassPhraseDialog;
+    int                 nPassPhraseDialogCur;
+    BOOL                bPassPhraseDialogOnce;
+    const char         *key_id;
+    const char         *pkey_file;
+} pphrase_cb_arg_t;
+
+#ifdef HAVE_ECC
+static const char *key_types[] = {"RSA", "DSA", "ECC"};
+#else
+static const char *key_types[] = {"RSA", "DSA"};
+#endif
+
+/*
+ * Return true if the named file exists and is readable
+ */
+
+static apr_status_t exists_and_readable(const char *fname, apr_pool_t *pool,
+                                        apr_time_t *mtime)
+{
+    apr_status_t stat;
+    apr_finfo_t sbuf;
+    apr_file_t *fd;
+
+    if ((stat = apr_stat(&sbuf, fname, APR_FINFO_MIN, pool)) != APR_SUCCESS)
+        return stat;
+
+    if (sbuf.filetype != APR_REG)
+        return APR_EGENERAL;
+
+    if ((stat = apr_file_open(&fd, fname, APR_READ, 0, pool)) != APR_SUCCESS)
+        return stat;
+
+    if (mtime) {
+        *mtime = sbuf.mtime;
+    }
+
+    apr_file_close(fd);
+    return APR_SUCCESS;
+}
+
+/*
+ * reuse vhost keys for asn1 tables where keys are allocated out
+ * of s->process->pool to prevent "leaking" each time we format
+ * a vhost key.  since the key is stored in a table with lifetime
+ * of s->process->pool, the key needs to have the same lifetime.
+ *
+ * XXX: probably seems silly to use a hash table with keys and values
+ * being the same, but it is easier than doing a linear search
+ * and will make it easier to remove keys if needed in the future.
+ * also have the problem with apr_array_header_t that if we
+ * underestimate the number of vhost keys when we apr_array_make(),
+ * the array will get resized when we push past the initial number
+ * of elts.  this resizing in the s->process->pool means "leaking"
+ * since apr_array_push() will apr_alloc arr->nalloc * 2 elts,
+ * leaving the original arr->elts to waste.
+ */
+static const char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
+                                  const char *id, int i)
+{
+    /* 'p' pool used here is cleared on restarts (or sooner) */
+    char *key = apr_psprintf(p, "%s:%d", id, i);
+    void *keyptr = apr_hash_get(mc->tVHostKeys, key,
+                                APR_HASH_KEY_STRING);
+
+    if (!keyptr) {
+        /* make a copy out of s->process->pool */
+        keyptr = apr_pstrdup(mc->pPool, key);
+        apr_hash_set(mc->tVHostKeys, keyptr,
+                     APR_HASH_KEY_STRING, keyptr);
+    }
+
+    return (char *)keyptr;
+}
+
+/*  _________________________________________________________________
+**
+**  Pass Phrase and Private Key Handling
+**  _________________________________________________________________
+*/
+
+#define BUILTIN_DIALOG_BACKOFF 2
+#define BUILTIN_DIALOG_RETRIES 5
+
+static apr_file_t *writetty = NULL;
+static apr_file_t *readtty = NULL;
+
+int ssl_pphrase_Handle_CB(char *, int, int, void *);
+
+static char *pphrase_array_get(apr_array_header_t *arr, int idx)
+{
+    if ((idx < 0) || (idx >= arr->nelts)) {
+        return NULL;
+    }
+
+    return ((char **)arr->elts)[idx];
+}
+
+apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
+                                     const char *pkey_file,
+                                     apr_array_header_t **pphrases)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
+    EVP_PKEY *pPrivateKey = NULL;
+    ssl_asn1_t *asn1;
+    unsigned char *ucp;
+    long int length;
+    BOOL bReadable;
+    int nPassPhrase = (*pphrases)->nelts;
+    int nPassPhraseRetry = 0;
+    apr_time_t pkey_mtime = 0;
+    apr_status_t rv;
+    pphrase_cb_arg_t ppcb_arg;
+
+    if (!pkey_file) {
+         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02573)
+                      "Init: No private key specified for %s", key_id);
+         return ssl_die(s);
+    }
+    else if ((rv = exists_and_readable(pkey_file, p, &pkey_mtime))
+             != APR_SUCCESS ) {
+         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02574)
+                      "Init: Can't open server private key file %s", pkey_file);
+         return ssl_die(s);
+    }
+
+    ppcb_arg.s                     = s;
+    ppcb_arg.p                     = p;
+    ppcb_arg.aPassPhrase           = *pphrases;
+    ppcb_arg.nPassPhraseCur        = 0;
+    ppcb_arg.cpPassPhraseCur       = NULL;
+    ppcb_arg.nPassPhraseDialog     = 0;
+    ppcb_arg.nPassPhraseDialogCur  = 0;
+    ppcb_arg.bPassPhraseDialogOnce = TRUE;
+    ppcb_arg.key_id                = key_id;
+    ppcb_arg.pkey_file             = pkey_file;
+
+    /*
+     * if the private key is encrypted and SSLPassPhraseDialog
+     * is configured to "builtin" it isn't possible to prompt for
+     * a password after httpd has detached from the tty.
+     * in this case if we already have a private key and the
+     * file name/mtime hasn't changed, then reuse the existing key.
+     * we also reuse existing private keys that were encrypted for
+     * exec: and pipe: dialogs to minimize chances to snoop the
+     * password.  that and pipe: dialogs might prompt the user
+     * for password, which on win32 for example could happen 4
+     * times at startup.  twice for each child and twice within
+     * each since apache "restarts itself" on startup.
+     * of course this will not work for the builtin dialog if
+     * the server was started without LoadModule ssl_module
+     * configured, then restarted with it configured.
+     * but we fall through with a chance of success if the key
+     * is not encrypted or can be handled via exec or pipe dialog.
+     * and in the case of fallthrough, pkey_mtime and isatty()
+     * are used to give a better idea as to what failed.
+     */
+    if (pkey_mtime) {
+        ssl_asn1_t *asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
+        if (asn1 && (asn1->source_mtime == pkey_mtime)) {
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02575)
+                         "Reusing existing private key from %s on restart",
+                         ppcb_arg.pkey_file);
+            return APR_SUCCESS;
+        }
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02576)
+                 "Attempting to load encrypted (?) private key %s", key_id);
+
+    for (;;) {
+        /*
+         * Try to read the private key file with the help of
+         * the callback function which serves the pass
+         * phrases to OpenSSL
+         */
+
+        ppcb_arg.cpPassPhraseCur = NULL;
+
+        /* Ensure that the error stack is empty; some SSL
+         * functions will fail spuriously if the error stack
+         * is not empty. */
+        ERR_clear_error();
+
+        bReadable = ((pPrivateKey = SSL_read_PrivateKey(ppcb_arg.pkey_file,
+                     NULL, ssl_pphrase_Handle_CB, &ppcb_arg)) != NULL ?
+                     TRUE : FALSE);
+
+        /*
+         * when the private key file now was readable,
+         * it's fine and we go out of the loop
+         */
+        if (bReadable)
+           break;
+
+        /*
+         * when we have more remembered pass phrases
+         * try to reuse these first.
+         */
+        if (ppcb_arg.nPassPhraseCur < nPassPhrase) {
+            ppcb_arg.nPassPhraseCur++;
+            continue;
+        }
+
+        /*
+         * else it's not readable and we have no more
+         * remembered pass phrases. Then this has to mean
+         * that the callback function popped up the dialog
+         * but a wrong pass phrase was entered.  We give the
+         * user (but not the dialog program) a few more
+         * chances...
+         */
+#ifndef WIN32
+        if ((sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+             || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE)
+#else
+        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE
+#endif
+            && ppcb_arg.cpPassPhraseCur != NULL
+            && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
+            apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect "
+                    "(%d more retr%s permitted).\n",
+                    (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
+                    (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
+            nPassPhraseRetry++;
+            if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
+                apr_sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)
+                            * 5 * APR_USEC_PER_SEC);
+            continue;
+        }
+#ifdef WIN32
+        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02577)
+                         "Init: SSLPassPhraseDialog builtin is not "
+                         "supported on Win32 (key file "
+                         "%s)", ppcb_arg.pkey_file);
+            return ssl_die(s);
+        }
+#endif /* WIN32 */
+
+        /*
+         * Ok, anything else now means a fatal error.
+         */
+        if (ppcb_arg.cpPassPhraseCur == NULL) {
+            if (ppcb_arg.nPassPhraseDialogCur && pkey_mtime &&
+                !isatty(fileno(stdout))) /* XXX: apr_isatty() */
+            {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+                             s, APLOGNO(02578)
+                             "Init: Unable to read pass phrase "
+                             "[Hint: key introduced or changed "
+                             "before restart?]");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+                             s, APLOGNO(02579) "Init: Private key not found");
+                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
+            }
+            if (writetty) {
+                apr_file_printf(writetty, "Apache:mod_ssl:Error: Private key not found.\n");
+                apr_file_printf(writetty, "**Stopped\n");
+            }
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02580)
+                         "Init: Pass phrase incorrect for key %s",
+                         key_id);
+            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+
+            if (writetty) {
+                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
+                apr_file_printf(writetty, "**Stopped\n");
+            }
+        }
+        return ssl_die(s);
+    }
+
+    if (pPrivateKey == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02581)
+                     "Init: Unable to read server private key from file %s",
+                     ppcb_arg.pkey_file);
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+        return ssl_die(s);
+    }
+
+    /*
+     * Log the type of reading
+     */
+    if (ppcb_arg.nPassPhraseDialogCur == 0) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02582)
+                     "unencrypted %s private key - pass phrase not "
+                     "required", key_id);
+    }
+    else {
+        if (ppcb_arg.cpPassPhraseCur != NULL) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                         s, APLOGNO(02583)
+                         "encrypted %s private key - pass phrase "
+                         "requested", key_id);
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                         s, APLOGNO(02584)
+                         "encrypted %s private key - pass phrase"
+                         " reused", key_id);
+        }
+    }
+
+    /*
+     * Ok, when we have one more pass phrase store it
+     */
+    if (ppcb_arg.cpPassPhraseCur != NULL) {
+        *(const char **)apr_array_push(ppcb_arg.aPassPhrase) =
+            ppcb_arg.cpPassPhraseCur;
+        nPassPhrase++;
+    }
+
+    /*
+     * Insert private key into the global module configuration
+     * (we convert it to a stand-alone DER byte sequence
+     * because the SSL library uses static variables inside a
+     * RSA structure which do not survive DSO reloads!)
+     */
+    length = i2d_PrivateKey(pPrivateKey, NULL);
+    ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length);
+    (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
+
+    if (ppcb_arg.nPassPhraseDialogCur != 0) {
+        /* remember mtime of encrypted keys */
+        asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
+        asn1->source_mtime = pkey_mtime;
+    }
+
+    /*
+     * Free the private key structure
+     */
+    EVP_PKEY_free(pPrivateKey);
+
+    /*
+     * Let the user know when we're successful.
+     */
+    if ((ppcb_arg.nPassPhraseDialog > 0) &&
+        (ppcb_arg.cpPassPhraseCur != NULL)) {
+        if (writetty) {
+            apr_file_printf(writetty, "\n"
+                            "OK: Pass Phrase Dialog successful.\n");
+        }
+    }
+
+    /* Close the pipes if they were opened
+     */
+    if (readtty) {
+        apr_file_close(readtty);
+        apr_file_close(writetty);
+        readtty = writetty = NULL;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t ssl_pipe_child_create(apr_pool_t *p, const char *progname)
+{
+    /* Child process code for 'ErrorLog "|..."';
+     * may want a common framework for this, since I expect it will
+     * be common for other foo-loggers to want this sort of thing...
+     */
+    apr_status_t rc;
+    apr_procattr_t *procattr;
+    apr_proc_t *procnew;
+
+    if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS) &&
+        ((rc = apr_procattr_io_set(procattr,
+                                   APR_FULL_BLOCK,
+                                   APR_FULL_BLOCK,
+                                   APR_NO_PIPE)) == APR_SUCCESS)) {
+        char **args;
+        const char *pname;
+
+        apr_tokenize_to_argv(progname, &args, p);
+        pname = apr_pstrdup(p, args[0]);
+        procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
+        rc = apr_proc_create(procnew, pname, (const char * const *)args,
+                             NULL, procattr, p);
+        if (rc == APR_SUCCESS) {
+            /* XXX: not sure if we aught to...
+             * apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
+             */
+            writetty = procnew->in;
+            readtty = procnew->out;
+        }
+    }
+
+    return rc;
+}
+
+static int pipe_get_passwd_cb(char *buf, int length, char *prompt, int verify)
+{
+    apr_status_t rc;
+    char *p;
+
+    apr_file_puts(prompt, writetty);
+
+    buf[0]='\0';
+    rc = apr_file_gets(buf, length, readtty);
+    apr_file_puts(APR_EOL_STR, writetty);
+
+    if (rc != APR_SUCCESS || apr_file_eof(readtty)) {
+        memset(buf, 0, length);
+        return 1;  /* failure */
+    }
+    if ((p = strchr(buf, '\n')) != NULL) {
+        *p = '\0';
+    }
+#ifdef WIN32
+    /* XXX: apr_sometest */
+    if ((p = strchr(buf, '\r')) != NULL) {
+        *p = '\0';
+    }
+#endif
+    return 0;
+}
+
+int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
+{
+    pphrase_cb_arg_t *ppcb_arg = (pphrase_cb_arg_t *)srv;
+    SSLSrvConfigRec *sc = mySrvConfig(ppcb_arg->s);
+    char *cpp;
+    int len = -1;
+
+    ppcb_arg->nPassPhraseDialog++;
+    ppcb_arg->nPassPhraseDialogCur++;
+
+    /*
+     * When remembered pass phrases are available use them...
+     */
+    if ((cpp = pphrase_array_get(ppcb_arg->aPassPhrase,
+                                 ppcb_arg->nPassPhraseCur)) != NULL) {
+        apr_cpystrn(buf, cpp, bufsize);
+        len = strlen(buf);
+        return len;
+    }
+
+    /*
+     * Builtin or Pipe dialog
+     */
+    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
+          || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+        char *prompt;
+        int i;
+
+        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+            if (!readtty) {
+                ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s,
+                             APLOGNO(01965)
+                             "Init: Creating pass phrase dialog pipe child "
+                             "'%s'", sc->server->pphrase_dialog_path);
+                if (ssl_pipe_child_create(ppcb_arg->p,
+                                          sc->server->pphrase_dialog_path)
+                        != APR_SUCCESS) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb_arg->s,
+                                 APLOGNO(01966)
+                                 "Init: Failed to create pass phrase pipe '%s'",
+                                 sc->server->pphrase_dialog_path);
+                    PEMerr(PEM_F_PEM_DEF_CALLBACK,
+                           PEM_R_PROBLEMS_GETTING_PASSWORD);
+                    memset(buf, 0, (unsigned int)bufsize);
+                    return (-1);
+                }
+            }
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01967)
+                         "Init: Requesting pass phrase via piped dialog");
+        }
+        else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+#ifdef WIN32
+            PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
+            memset(buf, 0, (unsigned int)bufsize);
+            return (-1);
+#else
+            /*
+             * stderr has already been redirected to the error_log.
+             * rather than attempting to temporarily rehook it to the terminal,
+             * we print the prompt to stdout before EVP_read_pw_string turns
+             * off tty echo
+             */
+            apr_file_open_stdout(&writetty, ppcb_arg->p);
+
+            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01968)
+                         "Init: Requesting pass phrase via builtin terminal "
+                         "dialog");
+#endif
+        }
+
+        /*
+         * The first time display a header to inform the user about what
+         * program he actually speaks to, which module is responsible for
+         * this terminal dialog and why to the hell he has to enter
+         * something...
+         */
+        if (ppcb_arg->nPassPhraseDialog == 1) {
+            apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
+                            AP_SERVER_BASEVERSION);
+            apr_file_printf(writetty, "Some of your private key files are encrypted for security reasons.\n");
+            apr_file_printf(writetty, "In order to read them you have to provide the pass phrases.\n");
+        }
+        if (ppcb_arg->bPassPhraseDialogOnce) {
+            ppcb_arg->bPassPhraseDialogOnce = FALSE;
+            apr_file_printf(writetty, "\n");
+            apr_file_printf(writetty, "Private key %s (%s)\n",
+                            ppcb_arg->key_id, ppcb_arg->pkey_file);
+        }
+
+        /*
+         * Emulate the OpenSSL internal pass phrase dialog
+         * (see crypto/pem/pem_lib.c:def_callback() for details)
+         */
+        prompt = "Enter pass phrase:";
+
+        for (;;) {
+            apr_file_puts(prompt, writetty);
+            if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
+                i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
+            }
+            else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
+                i = EVP_read_pw_string(buf, bufsize, "", FALSE);
+            }
+            if (i != 0) {
+                PEMerr(PEM_F_PEM_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
+                memset(buf, 0, (unsigned int)bufsize);
+                return (-1);
+            }
+            len = strlen(buf);
+            if (len < 1)
+                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n");
+            else
+                break;
+        }
+    }
+
+    /*
+     * Filter program
+     */
+    else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
+        const char *cmd = sc->server->pphrase_dialog_path;
+        const char **argv = apr_palloc(ppcb_arg->p, sizeof(char *) * 4);
+        const char *idx = ap_strrchr_c(ppcb_arg->key_id, ':') + 1;
+        char *result;
+        int i;
+
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01969)
+                     "Init: Requesting pass phrase from dialog filter "
+                     "program (%s)", cmd);
+
+        argv[0] = cmd;
+        argv[1] = apr_pstrndup(ppcb_arg->p, ppcb_arg->key_id,
+                               idx-1 - ppcb_arg->key_id);
+        if ((i = atoi(idx)) < CERTKEYS_IDX_MAX+1) {
+            /*
+             * For compatibility with existing 2.4.x configurations, use
+             * "RSA", "DSA" and "ECC" strings for the first two/three keys
+             */
+            argv[2] = key_types[i];
+        } else {
+            /* Four and above: use the integer index */
+            argv[2] = apr_pstrdup(ppcb_arg->p, idx);
+        }
+        argv[3] = NULL;
+
+        result = ssl_util_readfilter(ppcb_arg->s, ppcb_arg->p, cmd, argv);
+        apr_cpystrn(buf, result, bufsize);
+        len = strlen(buf);
+    }
+
+    /*
+     * Ok, we now have the pass phrase, so give it back
+     */
+    ppcb_arg->cpPassPhraseCur = apr_pstrdup(ppcb_arg->p, buf);
+
+    /*
+     * And return its length to OpenSSL...
+     */
+    return (len);
+}
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_rand.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_rand.c
new file mode 100644 (file)
index 0000000..df25d8f
--- /dev/null
@@ -0,0 +1,175 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_rand.c
+ *  Random Number Generator Seeding
+ */
+                             /* ``The generation of random
+                                  numbers is too important
+                                  to be left to chance.'' */
+
+#include "ssl_private.h"
+
+/*  _________________________________________________________________
+**
+**  Support for better seeding of SSL library's RNG
+**  _________________________________________________________________
+*/
+
+static int ssl_rand_choosenum(int, int);
+static int ssl_rand_feedfp(apr_pool_t *, apr_file_t *, int);
+
+int ssl_rand_seed(server_rec *s, apr_pool_t *p, ssl_rsctx_t nCtx, char *prefix)
+{
+    SSLModConfigRec *mc;
+    apr_array_header_t *apRandSeed;
+    ssl_randseed_t *pRandSeeds;
+    ssl_randseed_t *pRandSeed;
+    unsigned char stackdata[256];
+    int nDone;
+    apr_file_t *fp;
+    int i, n, l;
+
+    mc = myModConfig(s);
+    nDone = 0;
+    apRandSeed = mc->aRandSeed;
+    pRandSeeds = (ssl_randseed_t *)apRandSeed->elts;
+    for (i = 0; i < apRandSeed->nelts; i++) {
+        pRandSeed = &pRandSeeds[i];
+        if (pRandSeed->nCtx == nCtx) {
+            if (pRandSeed->nSrc == SSL_RSSRC_FILE) {
+                /*
+                 * seed in contents of an external file
+                 */
+                if (apr_file_open(&fp, pRandSeed->cpPath,
+                                  APR_READ, APR_OS_DEFAULT, p) != APR_SUCCESS)
+                    continue;
+                nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+                apr_file_close(fp);
+            }
+            else if (pRandSeed->nSrc == SSL_RSSRC_EXEC) {
+                const char *cmd = pRandSeed->cpPath;
+                const char **argv = apr_palloc(p, sizeof(char *) * 3);
+                /*
+                 * seed in contents generated by an external program
+                 */
+                argv[0] = cmd;
+                argv[1] = apr_itoa(p, pRandSeed->nBytes);
+                argv[2] = NULL;
+
+                if ((fp = ssl_util_ppopen(s, p, cmd, argv)) == NULL)
+                    continue;
+                nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+                ssl_util_ppclose(s, p, fp);
+            }
+            else if (pRandSeed->nSrc == SSL_RSSRC_EGD) {
+                /*
+                 * seed in contents provided by the external
+                 * Entropy Gathering Daemon (EGD)
+                 */
+                if ((n = RAND_egd(pRandSeed->cpPath)) == -1)
+                    continue;
+                nDone += n;
+            }
+            else if (pRandSeed->nSrc == SSL_RSSRC_BUILTIN) {
+                struct {
+                    time_t t;
+                    pid_t pid;
+                } my_seed;
+
+                /*
+                 * seed in the current time (usually just 4 bytes)
+                 */
+                my_seed.t = time(NULL);
+
+                /*
+                 * seed in the current process id (usually just 4 bytes)
+                 */
+                my_seed.pid = mc->pid;
+
+                l = sizeof(my_seed);
+                RAND_seed((unsigned char *)&my_seed, l);
+                nDone += l;
+
+                /*
+                 * seed in some current state of the run-time stack (128 bytes)
+                 */
+                n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1);
+                RAND_seed(stackdata+n, 128);
+                nDone += 128;
+
+            }
+        }
+    }
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
+                 "%sSeeding PRNG with %d bytes of entropy", prefix, nDone);
+
+    if (RAND_status() == 0)
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01990)
+                     "%sPRNG still contains insufficient entropy!", prefix);
+
+    return nDone;
+}
+
+#define BUFSIZE 8192
+
+static int ssl_rand_feedfp(apr_pool_t *p, apr_file_t *fp, int nReq)
+{
+    apr_size_t nDone;
+    unsigned char caBuf[BUFSIZE];
+    apr_size_t nBuf;
+    apr_size_t nRead;
+    apr_size_t nTodo;
+
+    nDone = 0;
+    nRead = BUFSIZE;
+    nTodo = nReq;
+    while (1) {
+        if (nReq > 0)
+            nRead = (nTodo < BUFSIZE ? nTodo : BUFSIZE);
+        nBuf = nRead;
+        if (apr_file_read(fp, caBuf, &nBuf) != APR_SUCCESS)
+            break;
+        RAND_seed(caBuf, nBuf);
+        nDone += nBuf;
+        if (nReq > 0) {
+            nTodo -= nBuf;
+            if (nTodo <= 0)
+                break;
+        }
+    }
+    return nDone;
+}
+
+static int ssl_rand_choosenum(int l, int h)
+{
+    int i;
+    char buf[50];
+
+    apr_snprintf(buf, sizeof(buf), "%.0f",
+                 (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
+    i = atoi(buf)+1;
+    if (i < l) i = l;
+    if (i > h) i = h;
+    return i;
+}
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_vars.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_engine_vars.c
new file mode 100644 (file)
index 0000000..922bf7c
--- /dev/null
@@ -0,0 +1,1081 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_engine_vars.c
+ *  Variable Lookup Facility
+ */
+                             /* ``Those of you who think they
+                                  know everything are very annoying
+                                  to those of us who do.''
+                                                  -- Unknown       */
+#include "ssl_private.h"
+#include "mod_ssl.h"
+#include "ap_expr.h"
+
+#include "apr_time.h"
+
+/*  _________________________________________________________________
+**
+**  Variable Lookup
+**  _________________________________________________________________
+*/
+
+static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, char *var);
+static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var);
+static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
+static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
+static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
+static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var);
+static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c);
+static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var);
+static void  ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
+static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
+static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
+
+static int ssl_is_https(conn_rec *c)
+{
+    SSLConnRec *sslconn = myConnConfig(c);
+    return sslconn && sslconn->ssl;
+}
+
+static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
+static char var_library_interface[] = SSL_LIBRARY_TEXT;
+static char *var_library = NULL;
+
+static apr_array_header_t *expr_peer_ext_list_fn(ap_expr_eval_ctx_t *ctx,
+                                                 const void *dummy,
+                                                 const char *arg)
+{
+    return ssl_ext_list(ctx->p, ctx->c, 1, arg);
+}
+
+static const char *expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
+{
+    char *var = (char *)data;
+    return ssl_var_lookup_ssl(ctx->p, ctx->c, ctx->r, var);
+}
+
+static int ssl_expr_lookup(ap_expr_lookup_parms *parms)
+{
+    switch (parms->type) {
+    case AP_EXPR_FUNC_VAR:
+        /* for now, we just handle everything that starts with SSL_, but
+         * register our hook as APR_HOOK_LAST
+         * XXX: This can be optimized
+         */
+        if (strcEQn(parms->name, "SSL_", 4)) {
+            *parms->func = expr_var_fn;
+            *parms->data = parms->name + 4;
+            return OK;
+        }
+        break;
+    case AP_EXPR_FUNC_LIST:
+        if (strcEQ(parms->name, "PeerExtList")) {
+            *parms->func = expr_peer_ext_list_fn;
+            *parms->data = "PeerExtList";
+            return OK;
+        }
+        break;
+    }
+    return DECLINED;
+}
+
+
+void ssl_var_register(apr_pool_t *p)
+{
+    char *cp, *cp2;
+
+    APR_REGISTER_OPTIONAL_FN(ssl_is_https);
+    APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
+    APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
+
+    /* Perform once-per-process library version determination: */
+    var_library = apr_pstrdup(p, SSL_LIBRARY_DYNTEXT);
+
+    if ((cp = strchr(var_library, ' ')) != NULL) {
+        *cp = '/';
+        if ((cp2 = strchr(cp, ' ')) != NULL)
+            *cp2 = NUL;
+    }
+
+    if ((cp = strchr(var_library_interface, ' ')) != NULL) {
+        *cp = '/';
+        if ((cp2 = strchr(cp, ' ')) != NULL)
+            *cp2 = NUL;
+    }
+
+    ap_hook_expr_lookup(ssl_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+/* This function must remain safe to use for a non-SSL connection. */
+char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    const char *result;
+    BOOL resdup;
+    apr_time_exp_t tm;
+
+    result = NULL;
+    resdup = TRUE;
+
+    /*
+     * When no pool is given try to find one
+     */
+    if (p == NULL) {
+        if (r != NULL)
+            p = r->pool;
+        else if (c != NULL)
+            p = c->pool;
+        else
+            p = mc->pPool;
+    }
+
+    /*
+     * Request dependent stuff
+     */
+    if (r != NULL) {
+        switch (var[0]) {
+        case 'H':
+        case 'h':
+            if (strcEQ(var, "HTTP_USER_AGENT"))
+                result = apr_table_get(r->headers_in, "User-Agent");
+            else if (strcEQ(var, "HTTP_REFERER"))
+                result = apr_table_get(r->headers_in, "Referer");
+            else if (strcEQ(var, "HTTP_COOKIE"))
+                result = apr_table_get(r->headers_in, "Cookie");
+            else if (strcEQ(var, "HTTP_FORWARDED"))
+                result = apr_table_get(r->headers_in, "Forwarded");
+            else if (strcEQ(var, "HTTP_HOST"))
+                result = apr_table_get(r->headers_in, "Host");
+            else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
+                result = apr_table_get(r->headers_in, "Proxy-Connection");
+            else if (strcEQ(var, "HTTP_ACCEPT"))
+                result = apr_table_get(r->headers_in, "Accept");
+            else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
+                /* all other headers from which we are still not know about */
+                result = apr_table_get(r->headers_in, var+5);
+            break;
+
+        case 'R':
+        case 'r':
+            if (strcEQ(var, "REQUEST_METHOD"))
+                result = r->method;
+            else if (strcEQ(var, "REQUEST_SCHEME"))
+                result = ap_http_scheme(r);
+            else if (strcEQ(var, "REQUEST_URI"))
+                result = r->uri;
+            else if (strcEQ(var, "REQUEST_FILENAME"))
+                result = r->filename;
+            else if (strcEQ(var, "REMOTE_ADDR"))
+                result = r->useragent_ip;
+            else if (strcEQ(var, "REMOTE_HOST"))
+                result = ap_get_remote_host(r->connection, r->per_dir_config,
+                                            REMOTE_NAME, NULL);
+            else if (strcEQ(var, "REMOTE_IDENT"))
+                result = ap_get_remote_logname(r);
+            else if (strcEQ(var, "REMOTE_USER"))
+                result = r->user;
+            break;
+
+        case 'S':
+        case 's':
+            if (strcEQn(var, "SSL", 3)) break; /* shortcut common case */
+
+            if (strcEQ(var, "SERVER_ADMIN"))
+                result = r->server->server_admin;
+            else if (strcEQ(var, "SERVER_NAME"))
+                result = ap_get_server_name_for_url(r);
+            else if (strcEQ(var, "SERVER_PORT"))
+                result = apr_psprintf(p, "%u", ap_get_server_port(r));
+            else if (strcEQ(var, "SERVER_PROTOCOL"))
+                result = r->protocol;
+            else if (strcEQ(var, "SCRIPT_FILENAME"))
+                result = r->filename;
+            break;
+
+        default:
+            if (strcEQ(var, "PATH_INFO"))
+                result = r->path_info;
+            else if (strcEQ(var, "QUERY_STRING"))
+                result = r->args;
+            else if (strcEQ(var, "IS_SUBREQ"))
+                result = (r->main != NULL ? "true" : "false");
+            else if (strcEQ(var, "DOCUMENT_ROOT"))
+                result = ap_document_root(r);
+            else if (strcEQ(var, "AUTH_TYPE"))
+                result = r->ap_auth_type;
+            else if (strcEQ(var, "THE_REQUEST"))
+                result = r->the_request;
+            else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
+                result = apr_table_get(r->notes, var+4);
+                if (result == NULL)
+                    result = apr_table_get(r->subprocess_env, var+4);
+            }
+            break;
+        }
+    }
+
+    /*
+     * Connection stuff
+     */
+    if (result == NULL && c != NULL) {
+        SSLConnRec *sslconn = myConnConfig(c);
+        if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
+            && sslconn && sslconn->ssl)
+            result = ssl_var_lookup_ssl(p, c, r, var+4);
+        else if (strcEQ(var, "HTTPS")) {
+            if (sslconn && sslconn->ssl)
+                result = "on";
+            else
+                result = "off";
+        }
+    }
+
+    /*
+     * Totally independent stuff
+     */
+    if (result == NULL) {
+        if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
+            result = ssl_var_lookup_ssl_version(p, var+12);
+        else if (strcEQ(var, "SERVER_SOFTWARE"))
+            result = ap_get_server_banner();
+        else if (strcEQ(var, "API_VERSION")) {
+            result = apr_itoa(p, MODULE_MAGIC_NUMBER);
+            resdup = FALSE;
+        }
+        else if (strcEQ(var, "TIME_YEAR")) {
+            apr_time_exp_lt(&tm, apr_time_now());
+            result = apr_psprintf(p, "%02d%02d",
+                                 (tm.tm_year / 100) + 19, tm.tm_year % 100);
+            resdup = FALSE;
+        }
+#define MKTIMESTR(format, tmfield) \
+            apr_time_exp_lt(&tm, apr_time_now()); \
+            result = apr_psprintf(p, format, tm.tmfield); \
+            resdup = FALSE;
+        else if (strcEQ(var, "TIME_MON")) {
+            MKTIMESTR("%02d", tm_mon+1)
+        }
+        else if (strcEQ(var, "TIME_DAY")) {
+            MKTIMESTR("%02d", tm_mday)
+        }
+        else if (strcEQ(var, "TIME_HOUR")) {
+            MKTIMESTR("%02d", tm_hour)
+        }
+        else if (strcEQ(var, "TIME_MIN")) {
+            MKTIMESTR("%02d", tm_min)
+        }
+        else if (strcEQ(var, "TIME_SEC")) {
+            MKTIMESTR("%02d", tm_sec)
+        }
+        else if (strcEQ(var, "TIME_WDAY")) {
+            MKTIMESTR("%d", tm_wday)
+        }
+        else if (strcEQ(var, "TIME")) {
+            apr_time_exp_lt(&tm, apr_time_now());
+            result = apr_psprintf(p,
+                        "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19,
+                        (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday,
+                        tm.tm_hour, tm.tm_min, tm.tm_sec);
+            resdup = FALSE;
+        }
+        /* all other env-variables from the parent Apache process */
+        else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
+            result = getenv(var+4);
+        }
+    }
+
+    if (result != NULL && resdup)
+        result = apr_pstrdup(p, result);
+    if (result == NULL)
+        result = "";
+    return (char *)result;
+}
+
+static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r,
+                                char *var)
+{
+    SSLConnRec *sslconn = myConnConfig(c);
+    char *result;
+    X509 *xs;
+    STACK_OF(X509) *sk;
+    SSL *ssl;
+
+    result = NULL;
+
+    ssl = sslconn->ssl;
+    if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
+        result = ssl_var_lookup_ssl_version(p, var+8);
+    }
+    else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
+        result = (char *)SSL_get_version(ssl);
+    }
+    else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
+        char buf[SSL_SESSION_ID_STRING_LEN];
+        SSL_SESSION *pSession = SSL_get_session(ssl);
+        if (pSession) {
+            unsigned char *id;
+            unsigned int idlen;
+
+#ifdef OPENSSL_NO_SSL_INTERN
+            id = (unsigned char *)SSL_SESSION_get_id(pSession, &idlen);
+#else
+            id = pSession->session_id;
+            idlen = pSession->session_id_length;
+#endif
+
+            result = apr_pstrdup(p, SSL_SESSION_id2sz(id, idlen,
+                                                      buf, sizeof(buf)));
+        }
+    }
+    else if(ssl != NULL && strcEQ(var, "SESSION_RESUMED")) {
+        if (SSL_session_reused(ssl) == 1)
+            result = "Resumed";
+        else
+            result = "Initial";
+    }
+    else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
+        result = ssl_var_lookup_ssl_cipher(p, c, var+6);
+    }
+    else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
+        sk = SSL_get_peer_cert_chain(ssl);
+        result = ssl_var_lookup_ssl_cert_chain(p, sk, var+18);
+    }
+    else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
+        result = ssl_var_lookup_ssl_cert_verify(p, c);
+    }
+    else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
+        if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
+            result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
+            X509_free(xs);
+        }
+    }
+    else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
+        if ((xs = SSL_get_certificate(ssl)) != NULL) {
+            result = ssl_var_lookup_ssl_cert(p, r, xs, var+7);
+            /* SSL_get_certificate is different from SSL_get_peer_certificate.
+             * No need to X509_free(xs).
+             */
+        }
+    }
+    else if (ssl != NULL && strcEQ(var, "COMPRESS_METHOD")) {
+        result = ssl_var_lookup_ssl_compress_meth(ssl);
+    }
+#ifdef HAVE_TLSEXT
+    else if (ssl != NULL && strcEQ(var, "TLS_SNI")) {
+        result = apr_pstrdup(p, SSL_get_servername(ssl,
+                                                   TLSEXT_NAMETYPE_host_name));
+    }
+#endif
+    else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
+        int flag = 0;
+#ifdef SSL_get_secure_renegotiation_support
+        flag = SSL_get_secure_renegotiation_support(ssl);
+#endif
+        result = apr_pstrdup(p, flag ? "true" : "false");
+    }
+#ifdef HAVE_SRP
+    else if (ssl != NULL && strcEQ(var, "SRP_USER")) {
+        if ((result = SSL_get_srp_username(ssl)) != NULL) {
+            result = apr_pstrdup(p, result);
+        }
+    }
+    else if (ssl != NULL && strcEQ(var, "SRP_USERINFO")) {
+        if ((result = SSL_get_srp_userinfo(ssl)) != NULL) {
+            result = apr_pstrdup(p, result);
+        }
+    }
+#endif
+
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_dn_oneline(apr_pool_t *p, request_rec *r,
+                                                X509_NAME *xsname)
+{
+    char *result = NULL;
+    SSLDirConfigRec *dc;
+    int legacy_format = 0;
+    if (r) {
+        dc = myDirConfig(r);
+        legacy_format = dc->nOptions & SSL_OPT_LEGACYDNFORMAT;
+    }
+    if (legacy_format) {
+        char *cp = X509_NAME_oneline(xsname, NULL, 0);
+        result = apr_pstrdup(p, cp);
+        OPENSSL_free(cp);
+    }
+    else {
+        BIO* bio;
+        int n;
+        unsigned long flags = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB;
+        if ((bio = BIO_new(BIO_s_mem())) == NULL)
+            return NULL;
+        X509_NAME_print_ex(bio, xsname, 0, flags);
+        n = BIO_pending(bio);
+        if (n > 0) {
+            result = apr_palloc(p, n+1);
+            n = BIO_read(bio, result, n);
+            result[n] = NUL;
+        }
+        BIO_free(bio);
+    }
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
+                                     char *var)
+{
+    char *result;
+    BOOL resdup;
+    X509_NAME *xsname;
+    int nid;
+
+    result = NULL;
+    resdup = TRUE;
+
+    if (strcEQ(var, "M_VERSION")) {
+        result = apr_psprintf(p, "%lu", X509_get_version(xs)+1);
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "M_SERIAL")) {
+        result = ssl_var_lookup_ssl_cert_serial(p, xs);
+    }
+    else if (strcEQ(var, "V_START")) {
+        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
+    }
+    else if (strcEQ(var, "V_END")) {
+        result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
+    }
+    else if (strcEQ(var, "V_REMAIN")) {
+        result = ssl_var_lookup_ssl_cert_remain(p, X509_get_notAfter(xs));
+        resdup = FALSE;
+    }
+    else if (*var && strcEQ(var+1, "_DN")) {
+        if (*var == 'S')
+            xsname = X509_get_subject_name(xs);
+        else if (*var == 'I')
+            xsname = X509_get_issuer_name(xs);
+        else
+            return NULL;
+        result = ssl_var_lookup_ssl_cert_dn_oneline(p, r, xsname);
+        resdup = FALSE;
+    }
+    else if (strlen(var) > 5 && strcEQn(var+1, "_DN_", 4)) {
+        if (*var == 'S')
+            xsname = X509_get_subject_name(xs);
+        else if (*var == 'I')
+            xsname = X509_get_issuer_name(xs);
+        else
+            return NULL;
+        result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "A_SIG")) {
+        nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
+        result = apr_pstrdup(p,
+                             (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "A_KEY")) {
+        nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->key->algor->algorithm));
+        result = apr_pstrdup(p,
+                             (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "CERT")) {
+        result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+    }
+
+    if (resdup)
+        result = apr_pstrdup(p, result);
+    return result;
+}
+
+/* In this table, .extract is non-zero if RDNs using the NID should be
+ * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
+ * variables. */
+static const struct {
+    char *name;
+    int   nid;
+    int   extract;
+} ssl_var_lookup_ssl_cert_dn_rec[] = {
+    { "C",     NID_countryName,            1 },
+    { "ST",    NID_stateOrProvinceName,    1 }, /* officially    (RFC2156) */
+    { "SP",    NID_stateOrProvinceName,    0 }, /* compatibility (SSLeay)  */
+    { "L",     NID_localityName,           1 },
+    { "O",     NID_organizationName,       1 },
+    { "OU",    NID_organizationalUnitName, 1 },
+    { "CN",    NID_commonName,             1 },
+    { "T",     NID_title,                  1 },
+    { "I",     NID_initials,               1 },
+    { "G",     NID_givenName,              1 },
+    { "S",     NID_surname,                1 },
+    { "D",     NID_description,            1 },
+#ifdef NID_userId
+    { "UID",   NID_userId,                 1 },
+#endif
+    { "Email", NID_pkcs9_emailAddress,     1 },
+    { NULL,    0,                          0 }
+};
+
+static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
+{
+    char *result, *ptr;
+    X509_NAME_ENTRY *xsne;
+    int i, j, n, idx = 0;
+    apr_size_t varlen;
+
+    /* if an _N suffix is used, find the Nth attribute of given name */
+    ptr = strchr(var, '_');
+    if (ptr != NULL && strspn(ptr + 1, "0123456789") == strlen(ptr + 1)) {
+        idx = atoi(ptr + 1);
+        varlen = ptr - var;
+    } else {
+        varlen = strlen(var);
+    }
+
+    result = NULL;
+
+    for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
+        if (strEQn(var, ssl_var_lookup_ssl_cert_dn_rec[i].name, varlen)
+            && strlen(ssl_var_lookup_ssl_cert_dn_rec[i].name) == varlen) {
+            for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
+                                                   xsname->entries);
+                 j++) {
+                xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
+                                                xsname->entries, j);
+
+                n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+
+                if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid && idx-- == 0) {
+                    result = SSL_X509_NAME_ENTRY_to_string(p, xsne);
+                    break;
+                }
+            }
+            break;
+        }
+    }
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
+{
+    char *result;
+    BIO* bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    ASN1_TIME_print(bio, tm);
+    n = BIO_pending(bio);
+    result = apr_pcalloc(p, n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = NUL;
+    BIO_free(bio);
+    return result;
+}
+
+#define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
+
+/* Return a string giving the number of days remaining until 'tm', or
+ * "0" if this can't be determined. */
+static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm)
+{
+    apr_time_t then, now = apr_time_now();
+    apr_time_exp_t exp = {0};
+    long diff;
+    unsigned char *dp;
+
+    /* Fail if the time isn't a valid ASN.1 TIME; RFC3280 mandates
+     * that the seconds digits are present even though ASN.1
+     * doesn't. */
+    if ((tm->type == V_ASN1_UTCTIME && tm->length < 11) ||
+        (tm->type == V_ASN1_GENERALIZEDTIME && tm->length < 13) ||
+        !ASN1_TIME_check(tm)) {
+        return apr_pstrdup(p, "0");
+    }
+
+    if (tm->type == V_ASN1_UTCTIME) {
+        exp.tm_year = DIGIT2NUM(tm->data);
+        if (exp.tm_year <= 50) exp.tm_year += 100;
+        dp = tm->data + 2;
+    } else {
+        exp.tm_year = DIGIT2NUM(tm->data) * 100 + DIGIT2NUM(tm->data + 2) - 1900;
+        dp = tm->data + 4;
+    }
+
+    exp.tm_mon = DIGIT2NUM(dp) - 1;
+    exp.tm_mday = DIGIT2NUM(dp + 2) + 1;
+    exp.tm_hour = DIGIT2NUM(dp + 4);
+    exp.tm_min = DIGIT2NUM(dp + 6);
+    exp.tm_sec = DIGIT2NUM(dp + 8);
+
+    if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS) {
+        return apr_pstrdup(p, "0");
+    }
+
+    diff = (long)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
+
+    return diff > 0 ? apr_ltoa(p, diff) : apr_pstrdup(p, "0");
+}
+
+static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs)
+{
+    char *result;
+    BIO *bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
+    n = BIO_pending(bio);
+    result = apr_pcalloc(p, n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = NUL;
+    BIO_free(bio);
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var)
+{
+    char *result;
+    X509 *xs;
+    int n;
+
+    result = NULL;
+
+    if (strspn(var, "0123456789") == strlen(var)) {
+        n = atoi(var);
+        if (n < sk_X509_num(sk)) {
+            xs = sk_X509_value(sk, n);
+            result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+        }
+    }
+
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs)
+{
+    char *result;
+    BIO *bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    PEM_write_bio_X509(bio, xs);
+    n = BIO_pending(bio);
+    result = apr_pcalloc(p, n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = NUL;
+    BIO_free(bio);
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c)
+{
+    SSLConnRec *sslconn = myConnConfig(c);
+    char *result;
+    long vrc;
+    const char *verr;
+    const char *vinfo;
+    SSL *ssl;
+    X509 *xs;
+
+    result = NULL;
+    ssl   = sslconn->ssl;
+    verr  = sslconn->verify_error;
+    vinfo = sslconn->verify_info;
+    vrc   = SSL_get_verify_result(ssl);
+    xs    = SSL_get_peer_certificate(ssl);
+
+    if (vrc == X509_V_OK && verr == NULL && xs == NULL)
+        /* no client verification done at all */
+        result = "NONE";
+    else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
+        /* client verification done successful */
+        result = "SUCCESS";
+    else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
+        /* client verification done in generous way */
+        result = "GENEROUS";
+    else
+        /* client verification failed */
+        result = apr_psprintf(p, "FAILED:%s",
+                              verr ? verr : X509_verify_cert_error_string(vrc));
+
+    if (xs)
+        X509_free(xs);
+    return result;
+}
+
+static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
+{
+    SSLConnRec *sslconn = myConnConfig(c);
+    char *result;
+    BOOL resdup;
+    int usekeysize, algkeysize;
+    SSL *ssl;
+
+    result = NULL;
+    resdup = TRUE;
+
+    ssl = sslconn->ssl;
+    ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
+
+    if (ssl && strEQ(var, "")) {
+        MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
+        result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
+    }
+    else if (strcEQ(var, "_EXPORT"))
+        result = (usekeysize < 56 ? "true" : "false");
+    else if (strcEQ(var, "_USEKEYSIZE")) {
+        result = apr_itoa(p, usekeysize);
+        resdup = FALSE;
+    }
+    else if (strcEQ(var, "_ALGKEYSIZE")) {
+        result = apr_itoa(p, algkeysize);
+        resdup = FALSE;
+    }
+
+    if (result != NULL && resdup)
+        result = apr_pstrdup(p, result);
+    return result;
+}
+
+static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
+{
+    MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
+
+    *usekeysize = 0;
+    *algkeysize = 0;
+    if (ssl != NULL)
+        if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
+            *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
+    return;
+}
+
+static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
+{
+    if (strEQ(var, "INTERFACE")) {
+        return apr_pstrdup(p, var_interface);
+    }
+    else if (strEQ(var, "LIBRARY_INTERFACE")) {
+        return apr_pstrdup(p, var_library_interface);
+    }
+    else if (strEQ(var, "LIBRARY")) {
+        return apr_pstrdup(p, var_library);
+    }
+    return NULL;
+}
+
+/* Add each RDN in 'xn' to the table 't' where the NID is present in
+ * 'nids', using key prefix 'pfx'.  */
+static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
+                       X509_NAME *xn, apr_pool_t *p)
+{
+    STACK_OF(X509_NAME_ENTRY) *ents = xn->entries;
+    X509_NAME_ENTRY *xsne;
+    apr_hash_t *count;
+    int i, nid;
+
+    /* Hash of (int) NID -> (int *) counter to count each time an RDN
+     * with the given NID has been seen. */
+    count = apr_hash_make(p);
+
+    /* For each RDN... */
+    for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
+         const char *tag;
+
+         xsne = sk_X509_NAME_ENTRY_value(ents, i);
+
+         /* Retrieve the nid, and check whether this is one of the nids
+          * which are to be extracted. */
+         nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+
+         tag = apr_hash_get(nids, &nid, sizeof nid);
+         if (tag) {
+             const char *key;
+             int *dup;
+             char *value;
+
+             /* Check whether a variable with this nid was already
+              * been used; if so, use the foo_N=bar syntax. */
+             dup = apr_hash_get(count, &nid, sizeof nid);
+             if (dup) {
+                 key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
+             }
+             else {
+                 /* Otherwise, use the plain foo=bar syntax. */
+                 dup = apr_pcalloc(p, sizeof *dup);
+                 apr_hash_set(count, &nid, sizeof nid, dup);
+                 key = apr_pstrcat(p, pfx, tag, NULL);
+             }
+             value = SSL_X509_NAME_ENTRY_to_string(p, xsne);
+             apr_table_setn(t, key, value);
+         }
+    }
+}
+
+void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
+{
+    apr_hash_t *nids;
+    unsigned n;
+    X509 *xs;
+
+    /* Build up a hash table of (int *)NID->(char *)short-name for all
+     * the tags which are to be extracted: */
+    nids = apr_hash_make(p);
+    for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
+        if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
+            apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
+                         sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
+                         ssl_var_lookup_ssl_cert_dn_rec[n].name);
+        }
+    }
+
+    /* Extract the server cert DNS -- note that the refcount does NOT
+     * increase: */
+    xs = SSL_get_certificate(ssl);
+    if (xs) {
+        extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
+        extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
+    }
+
+    /* Extract the client cert DNs -- note that the refcount DOES
+     * increase: */
+    xs = SSL_get_peer_certificate(ssl);
+    if (xs) {
+        extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
+        extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
+        X509_free(xs);
+    }
+}
+
+/* For an extension type which OpenSSL does not recognize, attempt to
+ * parse the extension type as a primitive string.  This will fail for
+ * any structured extension type per the docs.  Returns non-zero on
+ * success and writes the string to the given bio. */
+static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str)
+{
+    const unsigned char *pp = str->data;
+    ASN1_STRING *ret = ASN1_STRING_new();
+    int rv = 0;
+
+    /* This allows UTF8String, IA5String, VisibleString, or BMPString;
+     * conversion to UTF-8 is forced. */
+    if (d2i_DISPLAYTEXT(&ret, &pp, str->length)) {
+        ASN1_STRING_print_ex(bio, ret, ASN1_STRFLGS_UTF8_CONVERT);
+        rv = 1;
+    }
+
+    ASN1_STRING_free(ret);
+    return rv;
+}
+
+apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
+                                 const char *extension)
+{
+    SSLConnRec *sslconn = myConnConfig(c);
+    SSL *ssl = NULL;
+    apr_array_header_t *array = NULL;
+    X509 *xs = NULL;
+    ASN1_OBJECT *oid = NULL;
+    int count = 0, j;
+
+    if (!sslconn || !sslconn->ssl || !extension) {
+        return NULL;
+    }
+    ssl = sslconn->ssl;
+
+    /* We accept the "extension" string to be converted as
+     * a long name (nsComment), short name (DN) or
+     * numeric OID (1.2.3.4).
+     */
+    oid = OBJ_txt2obj(extension, 0);
+    if (!oid) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01970)
+                      "could not parse OID '%s'", extension);
+        ERR_clear_error();
+        return NULL;
+    }
+
+    xs = peer ? SSL_get_peer_certificate(ssl) : SSL_get_certificate(ssl);
+    if (xs == NULL) {
+        return NULL;
+    }
+
+    count = X509_get_ext_count(xs);
+    /* Create an array large enough to accomodate every extension. This is
+     * likely overkill, but safe.
+     */
+    array = apr_array_make(p, count, sizeof(char *));
+    for (j = 0; j < count; j++) {
+        X509_EXTENSION *ext = X509_get_ext(xs, j);
+
+        if (OBJ_cmp(ext->object, oid) == 0) {
+            BIO *bio = BIO_new(BIO_s_mem());
+
+            /* We want to obtain a string representation of the extensions
+             * value and add it to the array we're building.
+             * X509V3_EXT_print() doesn't know about all the possible
+             * data types, but the value is stored as an ASN1_OCTET_STRING
+             * allowing us a fallback in case of X509V3_EXT_print
+             * not knowing how to handle the data.
+             */
+            if (X509V3_EXT_print(bio, ext, 0, 0) == 1 ||
+                dump_extn_value(bio, X509_EXTENSION_get_data(ext)) == 1) {
+                BUF_MEM *buf;
+                char **ptr = apr_array_push(array);
+                BIO_get_mem_ptr(bio, &buf);
+                *ptr = apr_pstrmemdup(p, buf->data, buf->length);
+            } else {
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01971)
+                              "Found an extension '%s', but failed to "
+                              "create a string from it", extension);
+            }
+            BIO_vfree(bio);
+        }
+    }
+
+    if (array->nelts == 0)
+        array = NULL;
+
+    if (peer) {
+        /* only SSL_get_peer_certificate raises the refcount */
+        X509_free(xs);
+    }
+
+    ASN1_OBJECT_free(oid);
+    ERR_clear_error();
+    return array;
+}
+
+static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl)
+{
+    char *result = "NULL";
+#ifndef OPENSSL_NO_COMP
+    SSL_SESSION *pSession = SSL_get_session(ssl);
+
+    if (pSession) {
+#ifdef OPENSSL_NO_SSL_INTERN
+        switch (SSL_SESSION_get_compress_id(pSession)) {
+#else
+        switch (pSession->compress_meth) {
+#endif
+        case 0:
+            /* default "NULL" already set */
+            break;
+
+            /* Defined by RFC 3749, deflate is coded by "1" */
+        case 1:
+            result = "DEFLATE";
+            break;
+
+            /* IANA assigned compression number for LZS */
+        case 0x40:
+            result = "LZS";
+            break;
+
+        default:
+            result = "UNKNOWN";
+            break;
+        }
+    }
+#endif
+    return result;
+}
+
+/*  _________________________________________________________________
+**
+**  SSL Extension to mod_log_config
+**  _________________________________________________________________
+*/
+
+#include "../../modules/loggers/mod_log_config.h"
+
+static const char *ssl_var_log_handler_c(request_rec *r, char *a);
+static const char *ssl_var_log_handler_x(request_rec *r, char *a);
+
+/*
+ * register us for the mod_log_config function registering phase
+ * to establish %{...}c and to be able to expand %{...}x variables.
+ */
+void ssl_var_log_config_register(apr_pool_t *p)
+{
+    static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
+
+    log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
+
+    if (log_pfn_register) {
+        log_pfn_register(p, "c", ssl_var_log_handler_c, 0);
+        log_pfn_register(p, "x", ssl_var_log_handler_x, 0);
+    }
+    return;
+}
+
+/*
+ * implement the %{..}c log function
+ * (we are the only function)
+ */
+static const char *ssl_var_log_handler_c(request_rec *r, char *a)
+{
+    SSLConnRec *sslconn = myConnConfig(r->connection);
+    char *result;
+
+    if (sslconn == NULL || sslconn->ssl == NULL)
+        return NULL;
+    result = NULL;
+    if (strEQ(a, "version"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL");
+    else if (strEQ(a, "cipher"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
+    else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN");
+    else if (strEQ(a, "issuerdn") || strEQ(a, "cacert"))
+        result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN");
+    else if (strEQ(a, "errcode"))
+        result = "-";
+    else if (strEQ(a, "errstr"))
+        result = (char *)sslconn->verify_error;
+    if (result != NULL && result[0] == NUL)
+        result = NULL;
+    return result;
+}
+
+/*
+ * extend the implementation of the %{..}x log function
+ * (there can be more functions)
+ */
+static const char *ssl_var_log_handler_x(request_rec *r, char *a)
+{
+    char *result;
+
+    result = ssl_var_lookup(r->pool, r->server, r->connection, r, a);
+    if (result != NULL && result[0] == NUL)
+        result = NULL;
+    return result;
+}
+
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_private.h b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_private.h
new file mode 100644 (file)
index 0000000..09e698b
--- /dev/null
@@ -0,0 +1,975 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SSL_PRIVATE_H
+#define SSL_PRIVATE_H
+
+/**
+ * @file  ssl_private.h
+ * @brief Internal interfaces private to mod_ssl.
+ *
+ * @defgroup MOD_SSL_PRIVATE Private
+ * @ingroup MOD_SSL
+ * @{
+ */
+
+/** Apache headers */
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_connection.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_vhost.h"
+#include "util_script.h"
+#include "util_filter.h"
+#include "util_ebcdic.h"
+#include "util_mutex.h"
+#include "apr.h"
+#include "apr_strings.h"
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+#include "apr_tables.h"
+#include "apr_lib.h"
+#include "apr_fnmatch.h"
+#include "apr_strings.h"
+#include "apr_global_mutex.h"
+#include "apr_optional.h"
+#include "ap_socache.h"
+#include "mod_auth.h"
+
+/* The #ifdef macros are only defined AFTER including the above
+ * therefore we cannot include these system files at the top  :-(
+ */
+#ifdef APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if APR_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h> /* needed for STDIN_FILENO et.al., at least on FreeBSD */
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE !FALSE
+#endif
+
+#ifndef BOOL
+#define BOOL unsigned int
+#endif
+
+#include "ap_expr.h"
+
+/* OpenSSL headers */
+#include <openssl/opensslv.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+/* must be defined before including ssl.h */
+#define OPENSSL_NO_SSL_INTERN
+#endif
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/ocsp.h>
+
+/* Avoid tripping over an engine build installed globally and detected
+ * when the user points at an explicit non-engine flavor of OpenSSL
+ */
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+#include <openssl/engine.h>
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x0090801f)
+#error mod_ssl requires OpenSSL 0.9.8a or later
+#endif
+
+/**
+ * ...shifting sands of OpenSSL...
+ * Note: when adding support for new OpenSSL features, avoid explicit
+ * version number checks whenever possible, and use "feature-based"
+ * detection instead (check for definitions of constants or functions)
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000)
+#define MODSSL_SSL_CIPHER_CONST const
+#define MODSSL_SSL_METHOD_CONST const
+#else
+#define MODSSL_SSL_CIPHER_CONST
+#define MODSSL_SSL_METHOD_CONST
+#endif
+
+#if defined(OPENSSL_FIPS)
+#define HAVE_FIPS
+#endif
+
+#if defined(SSL_OP_NO_TLSv1_2)
+#define HAVE_TLSV1_X
+#endif
+
+#if defined(SSL_CONF_FLAG_FILE)
+#define HAVE_SSL_CONF_CMD
+#endif
+
+/**
+  * The following features all depend on TLS extension support.
+  * Within this block, check again for features (not version numbers).
+  */
+#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name)
+
+#define HAVE_TLSEXT
+
+/* ECC: make sure we have at least 1.0.0 */
+#if !defined(OPENSSL_NO_EC) && defined(TLSEXT_ECPOINTFORMAT_uncompressed)
+#define HAVE_ECC
+#endif
+
+/* OCSP stapling */
+#if !defined(OPENSSL_NO_OCSP) && defined(SSL_CTX_set_tlsext_status_cb)
+#define HAVE_OCSP_STAPLING
+#ifndef sk_OPENSSL_STRING_pop
+#define sk_OPENSSL_STRING_pop sk_pop
+#endif
+#endif
+
+/* TLS session tickets */
+#if defined(SSL_CTX_set_tlsext_ticket_key_cb)
+#define HAVE_TLS_SESSION_TICKETS
+#define TLSEXT_TICKET_KEY_LEN 48
+#ifndef tlsext_tick_md
+#ifdef OPENSSL_NO_SHA256
+#define tlsext_tick_md EVP_sha1
+#else
+#define tlsext_tick_md EVP_sha256
+#endif
+#endif
+#endif
+
+/* ALPN Protocol Negotiation */
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT)
+#define HAVE_TLS_ALPN
+#endif
+
+/* Next Protocol Negotiation */
+#if !defined(OPENSSL_NO_NEXTPROTONEG) && defined(OPENSSL_NPN_NEGOTIATED)
+#define HAVE_TLS_NPN
+#endif
+
+/* Secure Remote Password */
+#if !defined(OPENSSL_NO_SRP) && defined(SSL_CTRL_SET_TLS_EXT_SRP_USERNAME_CB)
+#define HAVE_SRP
+#include <openssl/srp.h>
+#endif
+
+#endif /* !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name) */
+
+/* mod_ssl headers */
+#include "ssl_util_ssl.h"
+
+APLOG_USE_MODULE(ssl);
+
+/*
+ * Provide reasonable default for some defines
+ */
+#ifndef PFALSE
+#define PFALSE ((void *)FALSE)
+#endif
+#ifndef PTRUE
+#define PTRUE ((void *)TRUE)
+#endif
+#ifndef UNSET
+#define UNSET (-1)
+#endif
+#ifndef NUL
+#define NUL '\0'
+#endif
+#ifndef RAND_MAX
+#include <limits.h>
+#define RAND_MAX INT_MAX
+#endif
+
+/**
+ * Provide reasonable defines for some types
+ */
+#ifndef UCHAR
+#define UCHAR unsigned char
+#endif
+
+/**
+ * Provide useful shorthands
+ */
+#define strEQ(s1,s2)     (strcmp(s1,s2)        == 0)
+#define strNE(s1,s2)     (strcmp(s1,s2)        != 0)
+#define strEQn(s1,s2,n)  (strncmp(s1,s2,n)     == 0)
+#define strNEn(s1,s2,n)  (strncmp(s1,s2,n)     != 0)
+
+#define strcEQ(s1,s2)    (strcasecmp(s1,s2)    == 0)
+#define strcNE(s1,s2)    (strcasecmp(s1,s2)    != 0)
+#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0)
+#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0)
+
+#define strIsEmpty(s)    (s == NULL || s[0] == NUL)
+
+#define myConnConfig(c) \
+(SSLConnRec *)ap_get_module_config(c->conn_config, &ssl_module)
+#define myCtxConfig(sslconn, sc) (sslconn->is_proxy ? sc->proxy : sc->server)
+#define myConnConfigSet(c, val) \
+ap_set_module_config(c->conn_config, &ssl_module, val)
+#define mySrvConfig(srv) (SSLSrvConfigRec *)ap_get_module_config(srv->module_config,  &ssl_module)
+#define myDirConfig(req) (SSLDirConfigRec *)ap_get_module_config(req->per_dir_config, &ssl_module)
+#define myModConfig(srv) (mySrvConfig((srv)))->mc
+#define mySrvFromConn(c) (myConnConfig(c))->server
+#define mySrvConfigFromConn(c) mySrvConfig(mySrvFromConn(c))
+#define myModConfigFromConn(c) myModConfig(mySrvFromConn(c))
+
+/**
+ * Defaults for the configuration
+ */
+#ifndef SSL_SESSION_CACHE_TIMEOUT
+#define SSL_SESSION_CACHE_TIMEOUT  300
+#endif
+
+/* Default setting for per-dir reneg buffer. */
+#ifndef DEFAULT_RENEG_BUFFER_SIZE
+#define DEFAULT_RENEG_BUFFER_SIZE (128 * 1024)
+#endif
+
+/* Default for OCSP response validity */
+#ifndef DEFAULT_OCSP_MAX_SKEW
+#define DEFAULT_OCSP_MAX_SKEW (60 * 5)
+#endif
+
+/* Default timeout for OCSP queries */
+#ifndef DEFAULT_OCSP_TIMEOUT
+#define DEFAULT_OCSP_TIMEOUT 10
+#endif
+
+/*
+ * For better backwards compatibility with the SSLCertificate[Key]File
+ * and SSLPassPhraseDialog ("exec" type) directives in 2.4.7 and earlier
+ */
+#ifdef HAVE_ECC
+#define CERTKEYS_IDX_MAX 2
+#else
+#define CERTKEYS_IDX_MAX 1
+#endif
+
+/**
+ * Define the SSL options
+ */
+#define SSL_OPT_NONE           (0)
+#define SSL_OPT_RELSET         (1<<0)
+#define SSL_OPT_STDENVVARS     (1<<1)
+#define SSL_OPT_EXPORTCERTDATA (1<<3)
+#define SSL_OPT_FAKEBASICAUTH  (1<<4)
+#define SSL_OPT_STRICTREQUIRE  (1<<5)
+#define SSL_OPT_OPTRENEGOTIATE (1<<6)
+#define SSL_OPT_LEGACYDNFORMAT (1<<7)
+typedef int ssl_opt_t;
+
+/**
+ * Define the SSL Protocol options
+ */
+#define SSL_PROTOCOL_NONE  (0)
+#define SSL_PROTOCOL_SSLV2 (1<<0)
+#define SSL_PROTOCOL_SSLV3 (1<<1)
+#define SSL_PROTOCOL_TLSV1 (1<<2)
+#ifdef HAVE_TLSV1_X
+#define SSL_PROTOCOL_TLSV1_1 (1<<3)
+#define SSL_PROTOCOL_TLSV1_2 (1<<4)
+#define SSL_PROTOCOL_ALL   (SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1| \
+                            SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2)
+#else
+#define SSL_PROTOCOL_ALL   (SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1)
+#endif
+typedef int ssl_proto_t;
+
+/**
+ * Define the SSL verify levels
+ */
+typedef enum {
+    SSL_CVERIFY_UNSET           = UNSET,
+    SSL_CVERIFY_NONE            = 0,
+    SSL_CVERIFY_OPTIONAL        = 1,
+    SSL_CVERIFY_REQUIRE         = 2,
+    SSL_CVERIFY_OPTIONAL_NO_CA  = 3
+} ssl_verify_t;
+
+#define SSL_VERIFY_PEER_STRICT \
+     (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+
+#define ssl_verify_error_is_optional(errnum) \
+   ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) \
+    || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) \
+    || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) \
+    || (errnum == X509_V_ERR_CERT_UNTRUSTED) \
+    || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
+
+/**
+  * CRL checking modes
+  */
+typedef enum {
+    SSL_CRLCHECK_UNSET = UNSET,
+    SSL_CRLCHECK_NONE  = 0,
+    SSL_CRLCHECK_LEAF  = 1,
+    SSL_CRLCHECK_CHAIN = 2
+} ssl_crlcheck_t;
+
+/**
+ * Define the SSL pass phrase dialog types
+ */
+typedef enum {
+    SSL_PPTYPE_UNSET   = UNSET,
+    SSL_PPTYPE_BUILTIN = 0,
+    SSL_PPTYPE_FILTER  = 1,
+    SSL_PPTYPE_PIPE    = 2
+} ssl_pphrase_t;
+
+/**
+ * Define the Path Checking modes
+ */
+#define SSL_PCM_EXISTS     1
+#define SSL_PCM_ISREG      2
+#define SSL_PCM_ISDIR      4
+#define SSL_PCM_ISNONZERO  8
+typedef unsigned int ssl_pathcheck_t;
+
+/**
+ * Define the SSL enabled state
+ */
+typedef enum {
+    SSL_ENABLED_UNSET    = UNSET,
+    SSL_ENABLED_FALSE    = 0,
+    SSL_ENABLED_TRUE     = 1,
+    SSL_ENABLED_OPTIONAL = 3
+} ssl_enabled_t;
+
+/**
+ * Define the SSL requirement structure
+ */
+typedef struct {
+    char           *cpExpr;
+    ap_expr_info_t *mpExpr;
+} ssl_require_t;
+
+/**
+ * Define the SSL random number generator seeding source
+ */
+typedef enum {
+    SSL_RSCTX_STARTUP = 1,
+    SSL_RSCTX_CONNECT = 2
+} ssl_rsctx_t;
+typedef enum {
+    SSL_RSSRC_BUILTIN = 1,
+    SSL_RSSRC_FILE    = 2,
+    SSL_RSSRC_EXEC    = 3,
+    SSL_RSSRC_EGD     = 4
+} ssl_rssrc_t;
+typedef struct {
+    ssl_rsctx_t  nCtx;
+    ssl_rssrc_t  nSrc;
+    char        *cpPath;
+    int          nBytes;
+} ssl_randseed_t;
+
+/**
+ * Define the structure of an ASN.1 anything
+ */
+typedef struct {
+    long int       nData;
+    unsigned char *cpData;
+    apr_time_t     source_mtime;
+} ssl_asn1_t;
+
+/**
+ * Define the mod_ssl per-module configuration structure
+ * (i.e. the global configuration for each httpd process)
+ */
+
+typedef enum {
+    SSL_SHUTDOWN_TYPE_UNSET,
+    SSL_SHUTDOWN_TYPE_STANDARD,
+    SSL_SHUTDOWN_TYPE_UNCLEAN,
+    SSL_SHUTDOWN_TYPE_ACCURATE
+} ssl_shutdown_type_e;
+
+typedef struct {
+    SSL *ssl;
+    const char *client_dn;
+    X509 *client_cert;
+    ssl_shutdown_type_e shutdown_type;
+    const char *verify_info;
+    const char *verify_error;
+    int verify_depth;
+    int is_proxy;
+    int disabled;
+    enum {
+        NON_SSL_OK = 0,        /* is SSL request, or error handling completed */
+        NON_SSL_SEND_HDR_SEP,  /* Need to send the header separator */
+        NON_SSL_SET_ERROR_MSG  /* Need to set the error message */
+    } non_ssl_request;
+
+    /* Track the handshake/renegotiation state for the connection so
+     * that all client-initiated renegotiations can be rejected, as a
+     * partial fix for CVE-2009-3555. */
+    enum {
+        RENEG_INIT = 0, /* Before initial handshake */
+        RENEG_REJECT, /* After initial handshake; any client-initiated
+                       * renegotiation should be rejected */
+        RENEG_ALLOW, /* A server-initiated renegotiation is taking
+                      * place (as dictated by configuration) */
+        RENEG_ABORT /* Renegotiation initiated by client, abort the
+                     * connection */
+    } reneg_state;
+
+#ifdef HAVE_TLS_NPN
+    /* Poor man's inter-module optional hooks for NPN. */
+    apr_array_header_t *alpn_proposefns; /* list of ssl_alpn_propose_protos callbacks */
+    apr_array_header_t *alpn_negofns; /* list of ssl_alpn_proto_negotiated callbacks. */
+#endif
+
+    server_rec *server;
+} SSLConnRec;
+
+/* BIG FAT WARNING: SSLModConfigRec has unusual memory lifetime: it is
+ * allocated out of the "process" pool and only a single such
+ * structure is created and used for the lifetime of the process.
+ * (The process pool is s->process->pool and is stored in the .pPool
+ * field.)  Most members of this structure are likewise allocated out
+ * of the process pool, but notably sesscache and sesscache_context
+ * are not.
+ *
+ * The structure is treated as mostly immutable after a single config
+ * parse has completed; the post_config hook (ssl_init_Module) flips
+ * the bFixed flag to true and subsequent invocations of the config
+ * callbacks hence do nothing.
+ *
+ * This odd lifetime strategy is used so that encrypted private keys
+ * can be decrypted once at startup and continue to be used across
+ * subsequent server reloads where the interactive password prompt is
+ * not possible.
+
+ * It is really an ABI nightmare waiting to happen since DSOs are
+ * reloaded across restarts, and nothing prevents the struct type
+ * changing across such reloads, yet the cached structure will be
+ * assumed to match regardless.
+ *
+ * This should really be fixed using a smaller structure which only
+ * stores that which is absolutely necessary (the private keys, maybe
+ * the random seed), and have that structure be strictly ABI-versioned
+ * for safety.
+ */
+typedef struct {
+    pid_t           pid;
+    apr_pool_t     *pPool;
+    BOOL            bFixed;
+
+    /* OpenSSL SSL_SESS_CACHE_* flags: */
+    long            sesscache_mode;
+
+    /* The configured provider, and associated private data
+     * structure. */
+    const ap_socache_provider_t *sesscache;
+    ap_socache_instance_t *sesscache_context;
+
+    apr_global_mutex_t   *pMutex;
+    apr_array_header_t   *aRandSeed;
+    apr_hash_t     *tVHostKeys;
+
+    /* A hash table of pointers to ssl_asn1_t structures.  The structures
+     * are used to store private keys in raw DER format (serialized OpenSSL
+     * PrivateKey structures).  The table is indexed by (vhost-id,
+     * index), for example the string "vhost.example.com:443:0". */
+    apr_hash_t     *tPrivateKey;
+
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
+    const char     *szCryptoDevice;
+#endif
+
+#ifdef HAVE_OCSP_STAPLING
+    const ap_socache_provider_t *stapling_cache;
+    ap_socache_instance_t *stapling_cache_context;
+    apr_global_mutex_t   *stapling_mutex;
+#endif
+} SSLModConfigRec;
+
+/** Structure representing configured filenames for certs and keys for
+ * a given vhost */
+typedef struct {
+    /* Lists of configured certs and keys for this server */
+    apr_array_header_t *cert_files;
+    apr_array_header_t *key_files;
+
+    /** Certificates which specify the set of CA names which should be
+     * sent in the CertificateRequest message: */
+    const char  *ca_name_path;
+    const char  *ca_name_file;
+} modssl_pk_server_t;
+
+typedef struct {
+    /** proxy can have any number of cert/key pairs */
+    const char  *cert_file;
+    const char  *cert_path;
+    const char  *ca_cert_file;
+    STACK_OF(X509_INFO) *certs; /* Contains End Entity certs */
+    STACK_OF(X509) **ca_certs; /* Contains ONLY chain certs for
+                                * each item in certs.
+                                * (ptr to array of ptrs) */
+} modssl_pk_proxy_t;
+
+/** stuff related to authentication that can also be per-dir */
+typedef struct {
+    /** known/trusted CAs */
+    const char  *ca_cert_path;
+    const char  *ca_cert_file;
+
+    const char  *cipher_suite;
+
+    /** for client or downstream server authentication */
+    int          verify_depth;
+    ssl_verify_t verify_mode;
+} modssl_auth_ctx_t;
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+typedef struct {
+    const char *file_path;
+    unsigned char key_name[16];
+    unsigned char hmac_secret[16];
+    unsigned char aes_key[16];
+} modssl_ticket_key_t;
+#endif
+
+#ifdef HAVE_SSL_CONF_CMD
+typedef struct {
+    const char *name;
+    const char *value;
+} ssl_ctx_param_t;
+#endif
+
+typedef struct SSLSrvConfigRec SSLSrvConfigRec;
+
+typedef struct {
+    SSLSrvConfigRec *sc; /** pointer back to server config */
+    SSL_CTX *ssl_ctx;
+
+    /** we are one or the other */
+    modssl_pk_server_t *pks;
+    modssl_pk_proxy_t  *pkp;
+
+#ifdef HAVE_TLS_SESSION_TICKETS
+    modssl_ticket_key_t *ticket_key;
+#endif
+
+    ssl_proto_t  protocol;
+
+    /** config for handling encrypted keys */
+    ssl_pphrase_t pphrase_dialog_type;
+    const char   *pphrase_dialog_path;
+
+    const char  *cert_chain;
+
+    /** certificate revocation list */
+    const char    *crl_path;
+    const char    *crl_file;
+    ssl_crlcheck_t crl_check_mode;
+
+#ifdef HAVE_OCSP_STAPLING
+    /** OCSP stapling options */
+    BOOL        stapling_enabled;
+    long        stapling_resptime_skew;
+    long        stapling_resp_maxage;
+    int         stapling_cache_timeout;
+    BOOL        stapling_return_errors;
+    BOOL        stapling_fake_trylater;
+    int         stapling_errcache_timeout;
+    apr_interval_time_t stapling_responder_timeout;
+    const char *stapling_force_url;
+#endif
+
+#ifdef HAVE_SRP
+    char *srp_vfile;
+    char *srp_unknown_user_seed;
+    SRP_VBASE  *srp_vbase;
+#endif
+
+    modssl_auth_ctx_t auth;
+
+    BOOL ocsp_enabled; /* true if OCSP verification enabled */
+    BOOL ocsp_force_default; /* true if the default responder URL is
+                              * used regardless of per-cert URL */
+    const char *ocsp_responder; /* default responder URL */
+    long ocsp_resptime_skew;
+    long ocsp_resp_maxage;
+    apr_interval_time_t ocsp_responder_timeout;
+    BOOL ocsp_use_request_nonce;
+
+#ifdef HAVE_SSL_CONF_CMD
+    SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
+    apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
+#endif
+  
+#if defined(HAVE_ALPN_NPN) || defined(HAVE_TLS_NPN)
+  apr_array_header_t *ssl_alpn_pref; /* protocol names in order of preference */
+#endif
+} modssl_ctx_t;
+
+struct SSLSrvConfigRec {
+    SSLModConfigRec *mc;
+    ssl_enabled_t    enabled;
+    BOOL             proxy_enabled;
+    const char      *vhost_id;
+    int              vhost_id_len;
+    int              session_cache_timeout;
+    BOOL             cipher_server_pref;
+    BOOL             insecure_reneg;
+    modssl_ctx_t    *server;
+    modssl_ctx_t    *proxy;
+    ssl_enabled_t    proxy_ssl_check_peer_expire;
+    ssl_enabled_t    proxy_ssl_check_peer_cn;
+    ssl_enabled_t    proxy_ssl_check_peer_name;
+#ifdef HAVE_TLSEXT
+    ssl_enabled_t    strict_sni_vhost_check;
+#endif
+#ifdef HAVE_FIPS
+    BOOL             fips;
+#endif
+#ifndef OPENSSL_NO_COMP
+    BOOL             compression;
+#endif
+};
+
+/**
+ * Define the mod_ssl per-directory configuration structure
+ * (i.e. the local configuration for all &lt;Directory>
+ *  and .htaccess contexts)
+ */
+typedef struct {
+    BOOL          bSSLRequired;
+    apr_array_header_t *aRequirement;
+    ssl_opt_t     nOptions;
+    ssl_opt_t     nOptionsAdd;
+    ssl_opt_t     nOptionsDel;
+    const char   *szCipherSuite;
+    ssl_verify_t  nVerifyClient;
+    int           nVerifyDepth;
+    const char   *szCACertificatePath;
+    const char   *szCACertificateFile;
+    const char   *szUserName;
+    apr_size_t    nRenegBufferSize;
+} SSLDirConfigRec;
+
+/**
+ *  function prototypes
+ */
+
+/**  API glue structures  */
+extern module AP_MODULE_DECLARE_DATA ssl_module;
+
+/**  configuration handling   */
+SSLModConfigRec *ssl_config_global_create(server_rec *);
+void         ssl_config_global_fix(SSLModConfigRec *);
+BOOL         ssl_config_global_isfixed(SSLModConfigRec *);
+void        *ssl_config_server_create(apr_pool_t *, server_rec *);
+void        *ssl_config_server_merge(apr_pool_t *, void *, void *);
+void        *ssl_config_perdir_create(apr_pool_t *, char *);
+void        *ssl_config_perdir_merge(apr_pool_t *, void *, void *);
+const char  *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *);
+const char  *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCertificateChainFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCACertificatePath(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCACertificateFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCADNRequestPath(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCADNRequestFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCARevocationPath(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCARevocationFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLCARevocationCheck(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag);
+const char  *ssl_cmd_SSLCompression(cmd_parms *, void *, int flag);
+const char  *ssl_cmd_SSLVerifyClient(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLVerifyDepth(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLSessionCache(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProtocol(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLOptions(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLRequireSSL(cmd_parms *, void *);
+const char  *ssl_cmd_SSLRequire(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLUserName(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg);
+const char  *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag);
+
+const char  *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag);
+const char  *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyVerify(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyCARevocationFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyCARevocationCheck(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *, void *, const char *);
+#ifdef HAVE_TLS_SESSION_TICKETS
+const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd, void *dcfg, const char *arg);
+#endif
+const char  *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag);
+const char  *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag);
+const char  *ssl_cmd_SSLProxyCheckPeerName(cmd_parms *cmd, void *dcfg, int flag);
+
+const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPResponseTimeSkew(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPResponseMaxAge(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPResponderTimeout(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLOCSPUseRequestNonce(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, int flag);
+
+#ifdef HAVE_SSL_CONF_CMD
+const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
+#endif
+
+#if defined(HAVE_ALPN_NPN) || defined(HAVE_TLS_NPN)
+const char *ssl_cmd_SSLAlpnPreference(cmd_parms *cmd, void *dcfg, const char *protocol);
+#endif
+
+#ifdef HAVE_SRP
+const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char *arg);
+const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const char *arg);
+#endif
+
+const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag);
+
+/**  module initialization  */
+apr_status_t ssl_init_Module(apr_pool_t *, apr_pool_t *, apr_pool_t *, server_rec *);
+apr_status_t ssl_init_Engine(server_rec *, apr_pool_t *);
+apr_status_t ssl_init_ConfigureServer(server_rec *, apr_pool_t *, apr_pool_t *, SSLSrvConfigRec *,
+                                      apr_array_header_t *);
+apr_status_t ssl_init_CheckServers(server_rec *, apr_pool_t *);
+STACK_OF(X509_NAME)
+            *ssl_init_FindCAList(server_rec *, apr_pool_t *, const char *, const char *);
+void         ssl_init_Child(apr_pool_t *, server_rec *);
+apr_status_t ssl_init_ModuleKill(void *data);
+
+/**  Apache API hooks  */
+int          ssl_hook_Auth(request_rec *);
+int          ssl_hook_UserCheck(request_rec *);
+int          ssl_hook_Access(request_rec *);
+int          ssl_hook_Fixup(request_rec *);
+int          ssl_hook_ReadReq(request_rec *);
+int          ssl_hook_Upgrade(request_rec *);
+void         ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s);
+
+/** Apache authz provisders */
+extern const authz_provider ssl_authz_provider_require_ssl;
+extern const authz_provider ssl_authz_provider_verify_client;
+
+/**  OpenSSL callbacks */
+DH          *ssl_callback_TmpDH(SSL *, int, int);
+int          ssl_callback_SSLVerify(int, X509_STORE_CTX *);
+int          ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, conn_rec *);
+int          ssl_callback_proxy_cert(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
+int          ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *);
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *);
+void         ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
+void         ssl_callback_Info(const SSL *, int, int);
+#ifdef HAVE_TLSEXT
+int          ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
+#endif
+#ifdef HAVE_TLS_SESSION_TICKETS
+int         ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *,
+                                       EVP_CIPHER_CTX *, HMAC_CTX *, int);
+#endif
+
+#ifdef HAVE_TLS_ALPN
+int ssl_callback_alpn_select(SSL *ssl, const unsigned char **out,
+                                                        unsigned char *outlen, const unsigned char *in,
+                                                        unsigned int inlen, void *arg);
+#elif defined(HAVE_TLS_NPN)
+int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg);
+#endif
+
+/**  Session Cache Support  */
+apr_status_t ssl_scache_init(server_rec *, apr_pool_t *);
+void         ssl_scache_status_register(apr_pool_t *p);
+void         ssl_scache_kill(server_rec *);
+BOOL         ssl_scache_store(server_rec *, UCHAR *, int,
+                              apr_time_t, SSL_SESSION *, apr_pool_t *);
+SSL_SESSION *ssl_scache_retrieve(server_rec *, UCHAR *, int, apr_pool_t *);
+void         ssl_scache_remove(server_rec *, UCHAR *, int,
+                               apr_pool_t *);
+
+/** Proxy Support */
+int ssl_proxy_enable(conn_rec *c);
+int ssl_engine_disable(conn_rec *c);
+
+/** OCSP Stapling Support */
+#ifdef HAVE_OCSP_STAPLING
+const char *ssl_cmd_SSLStaplingCache(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLUseStapling(cmd_parms *, void *, int);
+const char *ssl_cmd_SSLStaplingResponseTimeSkew(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingResponseMaxAge(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingStandardCacheTimeout(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingErrorCacheTimeout(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLStaplingReturnResponderErrors(cmd_parms *, void *, int);
+const char *ssl_cmd_SSLStaplingFakeTryLater(cmd_parms *, void *, int);
+const char *ssl_cmd_SSLStaplingResponderTimeout(cmd_parms *, void *, const char *);
+const char  *ssl_cmd_SSLStaplingForceURL(cmd_parms *, void *, const char *);
+apr_status_t modssl_init_stapling(server_rec *, apr_pool_t *, apr_pool_t *, modssl_ctx_t *);
+void         ssl_stapling_ex_init(void);
+int          ssl_stapling_init_cert(server_rec *s, modssl_ctx_t *mctx, X509 *x);
+#endif
+#ifdef HAVE_SRP
+int          ssl_callback_SRPServerParams(SSL *, int *, void *);
+#endif
+
+/**  I/O  */
+void         ssl_io_filter_init(conn_rec *, request_rec *r, SSL *);
+void         ssl_io_filter_register(apr_pool_t *);
+long         ssl_io_data_cb(BIO *, int, const char *, int, long, long);
+
+/* ssl_io_buffer_fill fills the setaside buffering of the HTTP request
+ * to allow an SSL renegotiation to take place. */
+int          ssl_io_buffer_fill(request_rec *r, apr_size_t maxlen);
+
+/**  PRNG  */
+int          ssl_rand_seed(server_rec *, apr_pool_t *, ssl_rsctx_t, char *);
+
+/**  Utility Functions  */
+char        *ssl_util_vhostid(apr_pool_t *, server_rec *);
+apr_file_t  *ssl_util_ppopen(server_rec *, apr_pool_t *, const char *,
+                             const char * const *);
+void         ssl_util_ppclose(server_rec *, apr_pool_t *, apr_file_t *);
+char        *ssl_util_readfilter(server_rec *, apr_pool_t *, const char *,
+                                 const char * const *);
+BOOL         ssl_util_path_check(ssl_pathcheck_t, const char *, apr_pool_t *);
+void         ssl_util_thread_setup(apr_pool_t *);
+int          ssl_init_ssl_connection(conn_rec *c, request_rec *r);
+
+/**  Pass Phrase Support  */
+apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
+                                     const char *, apr_array_header_t **);
+
+/**  Diffie-Hellman Parameter Support  */
+DH           *ssl_dh_GetParamFromFile(const char *);
+#ifdef HAVE_ECC
+EC_GROUP     *ssl_ec_GetParamFromFile(const char *);
+#endif
+
+unsigned char *ssl_asn1_table_set(apr_hash_t *table,
+                                  const char *key,
+                                  long int length);
+
+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
+                               const char *key);
+
+void ssl_asn1_table_unset(apr_hash_t *table,
+                          const char *key);
+
+/**  Mutex Support  */
+int          ssl_mutex_init(server_rec *, apr_pool_t *);
+int          ssl_mutex_reinit(server_rec *, apr_pool_t *);
+int          ssl_mutex_on(server_rec *);
+int          ssl_mutex_off(server_rec *);
+
+int          ssl_stapling_mutex_reinit(server_rec *, apr_pool_t *);
+
+/* mutex type names for Mutex directive */
+#define SSL_CACHE_MUTEX_TYPE    "ssl-cache"
+#define SSL_STAPLING_MUTEX_TYPE "ssl-stapling"
+
+apr_status_t ssl_die(server_rec *);
+
+/**  Logfile Support  */
+void         ssl_log_ssl_error(const char *, int, int, server_rec *);
+
+/* ssl_log_xerror, ssl_log_cxerror and ssl_log_rxerror are wrappers for the
+ * respective ap_log_*error functions and take a certificate as an
+ * additional argument (whose details are appended to the log message).
+ * The other arguments are interpreted exactly as with their ap_log_*error
+ * counterparts. */
+void ssl_log_xerror(const char *file, int line, int level,
+                    apr_status_t rv, apr_pool_t *p, server_rec *s,
+                    X509 *cert, const char *format, ...)
+    __attribute__((format(printf,8,9)));
+
+void ssl_log_cxerror(const char *file, int line, int level,
+                     apr_status_t rv, conn_rec *c, X509 *cert,
+                     const char *format, ...)
+    __attribute__((format(printf,7,8)));
+
+void ssl_log_rxerror(const char *file, int line, int level,
+                     apr_status_t rv, request_rec *r, X509 *cert,
+                     const char *format, ...)
+    __attribute__((format(printf,7,8)));
+
+#define SSLLOG_MARK              __FILE__,__LINE__
+
+/**  Variables  */
+
+/* Register variables for the lifetime of the process pool 'p'. */
+void         ssl_var_register(apr_pool_t *p);
+char        *ssl_var_lookup(apr_pool_t *, server_rec *, conn_rec *, request_rec *, char *);
+apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer, const char *extension);
+
+void         ssl_var_log_config_register(apr_pool_t *p);
+
+/* Extract SSL_*_DN_* variables into table 't' from SSL object 'ssl',
+ * allocating from 'p': */
+void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p);
+
+#ifndef OPENSSL_NO_OCSP
+/* Perform OCSP validation of the current cert in the given context.
+ * Returns non-zero on success or zero on failure.  On failure, the
+ * context error code is set. */
+int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc,
+                       server_rec *s, conn_rec *c, apr_pool_t *pool);
+
+/* OCSP helper interface; dispatches the given OCSP request to the
+ * responder at the given URI.  Returns the decoded OCSP response
+ * object, or NULL on error (in which case, errors will have been
+ * logged).  Pool 'p' is used for temporary allocations. */
+OCSP_RESPONSE *modssl_dispatch_ocsp_request(const apr_uri_t *uri,
+                                            apr_interval_time_t timeout,
+                                            OCSP_REQUEST *request,
+                                            conn_rec *c, apr_pool_t *p);
+#endif
+
+/* Retrieve DH parameters for given key length.  Return value should
+ * be treated as unmutable, since it is stored in process-global
+ * memory. */
+DH *modssl_get_dh_params(unsigned keylen);
+
+#endif /* SSL_PRIVATE_H */
+/** @} */
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_scache.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_scache.c
new file mode 100644 (file)
index 0000000..01f7254
--- /dev/null
@@ -0,0 +1,231 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_scache.c
+ *  Session Cache Abstraction
+ */
+                             /* ``Open-Source Software: generous
+                                  programmers from around the world all
+                                  join forces to help you shoot
+                                  yourself in the foot for free.''
+                                                 -- Unknown         */
+#include "ssl_private.h"
+#include "mod_status.h"
+
+/*  _________________________________________________________________
+**
+**  Session Cache: Common Abstraction Layer
+**  _________________________________________________________________
+*/
+
+apr_status_t ssl_scache_init(server_rec *s, apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+    struct ap_socache_hints hints;
+
+    /* The very first invocation of this function will be the
+     * post_config invocation during server startup; do nothing for
+     * this first (and only the first) time through, since the pool
+     * will be immediately cleared anyway.  For every subsequent
+     * invocation, initialize the configured cache. */
+    if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
+        return APR_SUCCESS;
+
+#ifdef HAVE_OCSP_STAPLING
+    if (mc->stapling_cache) {
+        memset(&hints, 0, sizeof hints);
+        hints.avg_obj_size = 1500;
+        hints.avg_id_len = 20;
+        hints.expiry_interval = 300;
+
+        rv = mc->stapling_cache->init(mc->stapling_cache_context,
+                                     "mod_ssl-stapling", &hints, s, p);
+        if (rv) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01872)
+                         "Could not initialize stapling cache. Exiting.");
+            return ssl_die(s);
+        }
+    }
+#endif
+
+    /*
+     * Warn the user that he should use the session cache.
+     * But we can operate without it, of course.
+     */
+    if (mc->sesscache == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01873)
+                     "Init: Session Cache is not configured "
+                     "[hint: SSLSessionCache]");
+        return APR_SUCCESS;
+    }
+
+    memset(&hints, 0, sizeof hints);
+    hints.avg_obj_size = 150;
+    hints.avg_id_len = 30;
+    hints.expiry_interval = 30;
+
+    rv = mc->sesscache->init(mc->sesscache_context, "mod_ssl-session", &hints, s, p);
+    if (rv) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01874)
+                     "Could not initialize session cache. Exiting.");
+        return ssl_die(s);
+    }
+
+    return APR_SUCCESS;
+}
+
+void ssl_scache_kill(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+
+    if (mc->sesscache) {
+        mc->sesscache->destroy(mc->sesscache_context, s);
+    }
+
+#ifdef HAVE_OCSP_STAPLING
+    if (mc->stapling_cache) {
+        mc->stapling_cache->destroy(mc->stapling_cache_context, s);
+    }
+#endif
+
+}
+
+BOOL ssl_scache_store(server_rec *s, UCHAR *id, int idlen,
+                      apr_time_t expiry, SSL_SESSION *sess,
+                      apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    unsigned char encoded[SSL_SESSION_MAX_DER], *ptr;
+    unsigned int len;
+    apr_status_t rv;
+
+    /* Serialise the session. */
+    len = i2d_SSL_SESSION(sess, NULL);
+    if (len > sizeof encoded) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01875)
+                     "session is too big (%u bytes)", len);
+        return FALSE;
+    }
+
+    ptr = encoded;
+    len = i2d_SSL_SESSION(sess, &ptr);
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_on(s);
+    }
+
+    rv = mc->sesscache->store(mc->sesscache_context, s, id, idlen,
+                              expiry, encoded, len, p);
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_off(s);
+    }
+
+    return rv == APR_SUCCESS ? TRUE : FALSE;
+}
+
+SSL_SESSION *ssl_scache_retrieve(server_rec *s, UCHAR *id, int idlen,
+                                 apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    unsigned char dest[SSL_SESSION_MAX_DER];
+    unsigned int destlen = SSL_SESSION_MAX_DER;
+    const unsigned char *ptr;
+    apr_status_t rv;
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_on(s);
+    }
+
+    rv = mc->sesscache->retrieve(mc->sesscache_context, s, id, idlen,
+                                 dest, &destlen, p);
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_off(s);
+    }
+
+    if (rv != APR_SUCCESS) {
+        return NULL;
+    }
+
+    ptr = dest;
+
+    return d2i_SSL_SESSION(NULL, &ptr, destlen);
+}
+
+void ssl_scache_remove(server_rec *s, UCHAR *id, int idlen,
+                       apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_on(s);
+    }
+
+    mc->sesscache->remove(mc->sesscache_context, s, id, idlen, p);
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_off(s);
+    }
+}
+
+/*  _________________________________________________________________
+**
+**  SSL Extension to mod_status
+**  _________________________________________________________________
+*/
+static int ssl_ext_status_hook(request_rec *r, int flags)
+{
+    SSLModConfigRec *mc = myModConfig(r->server);
+
+    if (mc == NULL || flags & AP_STATUS_SHORT || mc->sesscache == NULL)
+        return OK;
+
+    ap_rputs("<hr>\n", r);
+    ap_rputs("<table cellspacing=0 cellpadding=0>\n", r);
+    ap_rputs("<tr><td bgcolor=\"#000000\">\n", r);
+    ap_rputs("<b><font color=\"#ffffff\" face=\"Arial,Helvetica\">SSL/TLS Session Cache Status:</font></b>\r", r);
+    ap_rputs("</td></tr>\n", r);
+    ap_rputs("<tr><td bgcolor=\"#ffffff\">\n", r);
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_on(r->server);
+    }
+
+    mc->sesscache->status(mc->sesscache_context, r, flags);
+
+    if (mc->sesscache->flags & AP_SOCACHE_FLAG_NOTMPSAFE) {
+        ssl_mutex_off(r->server);
+    }
+
+    ap_rputs("</td></tr>\n", r);
+    ap_rputs("</table>\n", r);
+    return OK;
+}
+
+void ssl_scache_status_register(apr_pool_t *p)
+{
+    APR_OPTIONAL_HOOK(ap, status_hook, ssl_ext_status_hook, NULL, NULL,
+                      APR_HOOK_MIDDLE);
+}
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util.c
new file mode 100644 (file)
index 0000000..476aa0b
--- /dev/null
@@ -0,0 +1,379 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_util.c
+ *  Utility Functions
+ */
+                             /* ``Every day of my life
+                                  I am forced to add another
+                                  name to the list of people
+                                  who piss me off!''
+                                            -- Calvin          */
+
+#include "ssl_private.h"
+#include "ap_mpm.h"
+#include "apr_thread_mutex.h"
+
+/*  _________________________________________________________________
+**
+**  Utility Functions
+**  _________________________________________________________________
+*/
+
+char *ssl_util_vhostid(apr_pool_t *p, server_rec *s)
+{
+    char *id;
+    SSLSrvConfigRec *sc;
+    char *host;
+    apr_port_t port;
+
+    host = s->server_hostname;
+    if (s->port != 0)
+        port = s->port;
+    else {
+        sc = mySrvConfig(s);
+        if (sc->enabled == TRUE)
+            port = DEFAULT_HTTPS_PORT;
+        else
+            port = DEFAULT_HTTP_PORT;
+    }
+    id = apr_psprintf(p, "%s:%lu", host, (unsigned long)port);
+    return id;
+}
+
+apr_file_t *ssl_util_ppopen(server_rec *s, apr_pool_t *p, const char *cmd,
+                            const char * const *argv)
+{
+    apr_procattr_t *procattr;
+    apr_proc_t *proc;
+
+    if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
+                            APR_FULL_BLOCK) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_dir_set(procattr,
+                             ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
+        return NULL;
+    proc = apr_pcalloc(p, sizeof(apr_proc_t));
+    if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
+        return NULL;
+    return proc->out;
+}
+
+void ssl_util_ppclose(server_rec *s, apr_pool_t *p, apr_file_t *fp)
+{
+    apr_file_close(fp);
+    return;
+}
+
+/*
+ * Run a filter program and read the first line of its stdout output
+ */
+char *ssl_util_readfilter(server_rec *s, apr_pool_t *p, const char *cmd,
+                          const char * const *argv)
+{
+    static char buf[MAX_STRING_LEN];
+    apr_file_t *fp;
+    apr_size_t nbytes = 1;
+    char c;
+    int k;
+
+    if ((fp = ssl_util_ppopen(s, p, cmd, argv)) == NULL)
+        return NULL;
+    /* XXX: we are reading 1 byte at a time here */
+    for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
+                && nbytes == 1 && (k < MAX_STRING_LEN-1)     ; ) {
+        if (c == '\n' || c == '\r')
+            break;
+        buf[k++] = c;
+    }
+    buf[k] = NUL;
+    ssl_util_ppclose(s, p, fp);
+
+    return buf;
+}
+
+BOOL ssl_util_path_check(ssl_pathcheck_t pcm, const char *path, apr_pool_t *p)
+{
+    apr_finfo_t finfo;
+
+    if (path == NULL)
+        return FALSE;
+    if (pcm & SSL_PCM_EXISTS && apr_stat(&finfo, path,
+                                APR_FINFO_TYPE|APR_FINFO_SIZE, p) != 0)
+        return FALSE;
+    AP_DEBUG_ASSERT((pcm & SSL_PCM_EXISTS) ||
+                    !(pcm & (SSL_PCM_ISREG|SSL_PCM_ISDIR|SSL_PCM_ISNONZERO)));
+    if (pcm & SSL_PCM_ISREG && finfo.filetype != APR_REG)
+        return FALSE;
+    if (pcm & SSL_PCM_ISDIR && finfo.filetype != APR_DIR)
+        return FALSE;
+    if (pcm & SSL_PCM_ISNONZERO && finfo.size <= 0)
+        return FALSE;
+    return TRUE;
+}
+
+/*
+ * certain key data needs to survive restarts,
+ * which are stored in the user data table of s->process->pool.
+ * to prevent "leaking" of this data, we use malloc/free
+ * rather than apr_palloc and these wrappers to help make sure
+ * we do not leak the malloc-ed data.
+ */
+unsigned char *ssl_asn1_table_set(apr_hash_t *table,
+                                  const char *key,
+                                  long int length)
+{
+    apr_ssize_t klen = strlen(key);
+    ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
+
+    /*
+     * if a value for this key already exists,
+     * reuse as much of the already malloc-ed data
+     * as possible.
+     */
+    if (asn1) {
+        if (asn1->nData != length) {
+            free(asn1->cpData); /* XXX: realloc? */
+            asn1->cpData = NULL;
+        }
+    }
+    else {
+        asn1 = ap_malloc(sizeof(*asn1));
+        asn1->source_mtime = 0; /* used as a note for encrypted private keys */
+        asn1->cpData = NULL;
+    }
+
+    asn1->nData = length;
+    if (!asn1->cpData) {
+        asn1->cpData = ap_malloc(length);
+    }
+
+    apr_hash_set(table, key, klen, asn1);
+
+    return asn1->cpData; /* caller will assign a value to this */
+}
+
+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
+                               const char *key)
+{
+    return (ssl_asn1_t *)apr_hash_get(table, key, APR_HASH_KEY_STRING);
+}
+
+void ssl_asn1_table_unset(apr_hash_t *table,
+                          const char *key)
+{
+    apr_ssize_t klen = strlen(key);
+    ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
+
+    if (!asn1) {
+        return;
+    }
+
+    if (asn1->cpData) {
+        free(asn1->cpData);
+    }
+    free(asn1);
+
+    apr_hash_set(table, key, klen, NULL);
+}
+
+#if APR_HAS_THREADS
+/*
+ * To ensure thread-safetyness in OpenSSL - work in progress
+ */
+
+static apr_thread_mutex_t **lock_cs;
+static int                  lock_num_locks;
+
+static void ssl_util_thr_lock(int mode, int type,
+                              const char *file, int line)
+{
+    if (type < lock_num_locks) {
+        if (mode & CRYPTO_LOCK) {
+            apr_thread_mutex_lock(lock_cs[type]);
+        }
+        else {
+            apr_thread_mutex_unlock(lock_cs[type]);
+        }
+    }
+}
+
+/* Dynamic lock structure */
+struct CRYPTO_dynlock_value {
+    apr_pool_t *pool;
+    const char* file;
+    int line;
+    apr_thread_mutex_t *mutex;
+};
+
+/* Global reference to the pool passed into ssl_util_thread_setup() */
+apr_pool_t *dynlockpool = NULL;
+
+/*
+ * Dynamic lock creation callback
+ */
+static struct CRYPTO_dynlock_value *ssl_dyn_create_function(const char *file,
+                                                     int line)
+{
+    struct CRYPTO_dynlock_value *value;
+    apr_pool_t *p;
+    apr_status_t rv;
+
+    /*
+     * We need a pool to allocate our mutex.  Since we can't clear
+     * allocated memory from a pool, create a subpool that we can blow
+     * away in the destruction callback.
+     */
+    apr_pool_create(&p, dynlockpool);
+    ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE1, 0, p,
+                  "Creating dynamic lock");
+
+    value = apr_palloc(p, sizeof(struct CRYPTO_dynlock_value));
+    value->pool = p;
+    /* Keep our own copy of the place from which we were created,
+       using our own pool. */
+    value->file = apr_pstrdup(p, file);
+    value->line = line;
+    rv = apr_thread_mutex_create(&(value->mutex), APR_THREAD_MUTEX_DEFAULT,
+                                p);
+    if (rv != APR_SUCCESS) {
+        ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_ERR, rv, p, APLOGNO(02186)
+                      "Failed to create thread mutex for dynamic lock");
+        apr_pool_destroy(p);
+        return NULL;
+    }
+    return value;
+}
+
+/*
+ * Dynamic locking and unlocking function
+ */
+
+static void ssl_dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
+                           const char *file, int line)
+{
+    apr_status_t rv;
+
+    if (mode & CRYPTO_LOCK) {
+        ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, 0, l->pool,
+                      "Acquiring mutex %s:%d", l->file, l->line);
+        rv = apr_thread_mutex_lock(l->mutex);
+        ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, rv, l->pool,
+                      "Mutex %s:%d acquired!", l->file, l->line);
+    }
+    else {
+        ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, 0, l->pool,
+                      "Releasing mutex %s:%d", l->file, l->line);
+        rv = apr_thread_mutex_unlock(l->mutex);
+        ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE3, rv, l->pool,
+                      "Mutex %s:%d released!", l->file, l->line);
+    }
+}
+
+/*
+ * Dynamic lock destruction callback
+ */
+static void ssl_dyn_destroy_function(struct CRYPTO_dynlock_value *l,
+                          const char *file, int line)
+{
+    apr_status_t rv;
+
+    ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_TRACE1, 0, l->pool,
+                  "Destroying dynamic lock %s:%d", l->file, l->line);
+    rv = apr_thread_mutex_destroy(l->mutex);
+    if (rv != APR_SUCCESS) {
+        ap_log_perror(file, line, APLOG_MODULE_INDEX, APLOG_ERR, rv, l->pool,
+                      APLOGNO(02192) "Failed to destroy mutex for dynamic "
+                      "lock %s:%d", l->file, l->line);
+    }
+
+    /* Trust that whomever owned the CRYPTO_dynlock_value we were
+     * passed has no future use for it...
+     */
+    apr_pool_destroy(l->pool);
+}
+
+static unsigned long ssl_util_thr_id(void)
+{
+    /* OpenSSL needs this to return an unsigned long.  On OS/390, the pthread
+     * id is a structure twice that big.  Use the TCB pointer instead as a
+     * unique unsigned long.
+     */
+#ifdef __MVS__
+    struct PSA {
+        char unmapped[540];
+        unsigned long PSATOLD;
+    } *psaptr = 0;
+
+    return psaptr->PSATOLD;
+#else
+    return (unsigned long) apr_os_thread_current();
+#endif
+}
+
+static apr_status_t ssl_util_thread_cleanup(void *data)
+{
+    CRYPTO_set_locking_callback(NULL);
+    CRYPTO_set_id_callback(NULL);
+
+    CRYPTO_set_dynlock_create_callback(NULL);
+    CRYPTO_set_dynlock_lock_callback(NULL);
+    CRYPTO_set_dynlock_destroy_callback(NULL);
+
+    dynlockpool = NULL;
+
+    /* Let the registered mutex cleanups do their own thing
+     */
+    return APR_SUCCESS;
+}
+
+void ssl_util_thread_setup(apr_pool_t *p)
+{
+    int i;
+
+    lock_num_locks = CRYPTO_num_locks();
+    lock_cs = apr_palloc(p, lock_num_locks * sizeof(*lock_cs));
+
+    for (i = 0; i < lock_num_locks; i++) {
+        apr_thread_mutex_create(&(lock_cs[i]), APR_THREAD_MUTEX_DEFAULT, p);
+    }
+
+    CRYPTO_set_id_callback(ssl_util_thr_id);
+
+    CRYPTO_set_locking_callback(ssl_util_thr_lock);
+
+    /* Set up dynamic locking scaffolding for OpenSSL to use at its
+     * convenience.
+     */
+    dynlockpool = p;
+    CRYPTO_set_dynlock_create_callback(ssl_dyn_create_function);
+    CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock_function);
+    CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy_function);
+
+    apr_pool_cleanup_register(p, NULL, ssl_util_thread_cleanup,
+                                       apr_pool_cleanup_null);
+}
+#endif
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ocsp.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ocsp.c
new file mode 100644 (file)
index 0000000..9016040
--- /dev/null
@@ -0,0 +1,319 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file implements an OCSP client including a toy HTTP/1.0
+ * client.  Once httpd depends on a real HTTP client library, most of
+ * this can be thrown away. */
+
+#include "ssl_private.h"
+
+#ifndef OPENSSL_NO_OCSP
+
+#include "apr_buckets.h"
+#include "apr_uri.h"
+
+/* Serialize an OCSP request which will be sent to the responder at
+ * given URI to a memory BIO object, which is returned. */
+static BIO *serialize_request(OCSP_REQUEST *req, const apr_uri_t *uri)
+{
+    BIO *bio;
+    int len;
+
+    len = i2d_OCSP_REQUEST(req, NULL);
+
+    bio = BIO_new(BIO_s_mem());
+
+    BIO_printf(bio, "POST %s%s%s HTTP/1.0\r\n"
+               "Host: %s:%d\r\n"
+               "Content-Type: application/ocsp-request\r\n"
+               "Content-Length: %d\r\n"
+               "\r\n",
+               uri->path ? uri->path : "/",
+               uri->query ? "?" : "", uri->query ? uri->query : "",
+               uri->hostname, uri->port, len);
+
+    if (i2d_OCSP_REQUEST_bio(bio, req) != 1) {
+        BIO_free(bio);
+        return NULL;
+    }
+
+    return bio;
+}
+
+/* Send the OCSP request serialized into BIO 'request' to the
+ * responder at given server given by URI.  Returns socket object or
+ * NULL on error. */
+static apr_socket_t *send_request(BIO *request, const apr_uri_t *uri,
+                                  apr_interval_time_t timeout,
+                                  conn_rec *c, apr_pool_t *p)
+{
+    apr_status_t rv;
+    apr_sockaddr_t *sa;
+    apr_socket_t *sd;
+    char buf[HUGE_STRING_LEN];
+    int len;
+
+    rv = apr_sockaddr_info_get(&sa, uri->hostname, APR_UNSPEC, uri->port, 0, p);
+    if (rv) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01972)
+                      "could not resolve address of OCSP responder %s",
+                      uri->hostinfo);
+        return NULL;
+    }
+
+    /* establish a connection to the OCSP responder */
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01973)
+                  "connecting to OCSP responder '%s'", uri->hostinfo);
+
+    /* Cycle through address until a connect() succeeds. */
+    for (; sa; sa = sa->next) {
+        rv = apr_socket_create(&sd, sa->family, SOCK_STREAM, APR_PROTO_TCP, p);
+        if (rv == APR_SUCCESS) {
+            apr_socket_timeout_set(sd, timeout);
+
+            rv = apr_socket_connect(sd, sa);
+            if (rv == APR_SUCCESS) {
+                break;
+            }
+            apr_socket_close(sd);
+        }
+    }
+
+    if (sa == NULL) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01974)
+                      "could not connect to OCSP responder '%s'",
+                      uri->hostinfo);
+        return NULL;
+    }
+
+    /* send the request and get a response */
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01975)
+                 "sending request to OCSP responder");
+
+    while ((len = BIO_read(request, buf, sizeof buf)) > 0) {
+        char *wbuf = buf;
+        apr_size_t remain = len;
+
+        do {
+            apr_size_t wlen = remain;
+
+            rv = apr_socket_send(sd, wbuf, &wlen);
+            wbuf += remain;
+            remain -= wlen;
+        } while (rv == APR_SUCCESS && remain > 0);
+
+        if (rv) {
+            apr_socket_close(sd);
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01976)
+                          "failed to send request to OCSP responder '%s'",
+                          uri->hostinfo);
+            return NULL;
+        }
+    }
+
+    return sd;
+}
+
+/* Return a pool-allocated NUL-terminated line, with CRLF stripped,
+ * read from brigade 'bbin' using 'bbout' as temporary storage. */
+static char *get_line(apr_bucket_brigade *bbout, apr_bucket_brigade *bbin,
+                      conn_rec *c, apr_pool_t *p)
+{
+    apr_status_t rv;
+    apr_size_t len;
+    char *line;
+
+    apr_brigade_cleanup(bbout);
+
+    rv = apr_brigade_split_line(bbout, bbin, APR_BLOCK_READ, 8192);
+    if (rv) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01977)
+                      "failed reading line from OCSP server");
+        return NULL;
+    }
+
+    rv = apr_brigade_pflatten(bbout, &line, &len, p);
+    if (rv) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01978)
+                      "failed reading line from OCSP server");
+        return NULL;
+    }
+
+    if (len == 0) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02321)
+                      "empty response from OCSP server");
+        return NULL;
+    }
+
+    if (line[len-1] != APR_ASCII_LF) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01979)
+                      "response header line too long from OCSP server");
+        return NULL;
+    }
+
+    line[len-1] = '\0';
+    if (len > 1 && line[len-2] == APR_ASCII_CR) {
+        line[len-2] = '\0';
+    }
+
+    return line;
+}
+
+/* Maximum values to prevent eating RAM forever. */
+#define MAX_HEADERS (256)
+#define MAX_CONTENT (2048 * 1024)
+
+/* Read the OCSP response from the socket 'sd', using temporary memory
+ * BIO 'bio', and return the decoded OCSP response object, or NULL on
+ * error. */
+static OCSP_RESPONSE *read_response(apr_socket_t *sd, BIO *bio, conn_rec *c,
+                                    apr_pool_t *p)
+{
+    apr_bucket_brigade *bb, *tmpbb;
+    OCSP_RESPONSE *response;
+    char *line;
+    apr_size_t count;
+    apr_int64_t code;
+
+    /* Using brigades for response parsing is much simpler than using
+     * apr_socket_* directly. */
+    bb = apr_brigade_create(p, c->bucket_alloc);
+    tmpbb = apr_brigade_create(p, c->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_socket_create(sd, c->bucket_alloc));
+
+    line = get_line(tmpbb, bb, c, p);
+    if (!line || strncmp(line, "HTTP/", 5)
+        || (line = ap_strchr(line, ' ')) == NULL
+        || (code = apr_atoi64(++line)) < 200 || code > 299) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01980)
+                      "bad response from OCSP server: %s",
+                      line ? line : "(none)");
+        return NULL;
+    }
+
+    /* Read till end of headers; don't have to even bother parsing the
+     * Content-Length since the server is obliged to close the
+     * connection after the response anyway for HTTP/1.0. */
+    count = 0;
+    while ((line = get_line(tmpbb, bb, c, p)) != NULL && line[0]
+           && ++count < MAX_HEADERS) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01981)
+                      "OCSP response header: %s", line);
+    }
+
+    if (count == MAX_HEADERS) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01982)
+                      "could not read response headers from OCSP server, "
+                      "exceeded maximum count (%u)", MAX_HEADERS);
+        return NULL;
+    }
+    else if (!line) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01983)
+                      "could not read response header from OCSP server");
+        return NULL;
+    }
+
+    /* Read the response body into the memory BIO. */
+    count = 0;
+    while (!APR_BRIGADE_EMPTY(bb)) {
+        const char *data;
+        apr_size_t len;
+        apr_status_t rv;
+        apr_bucket *e = APR_BRIGADE_FIRST(bb);
+
+        rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+        if (rv == APR_EOF) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01984)
+                          "OCSP response: got EOF");
+            break;
+        }
+        if (rv != APR_SUCCESS) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01985)
+                          "error reading response from OCSP server");
+            return NULL;
+        }
+        if (len == 0) {
+            /* Ignore zero-length buckets (possible side-effect of
+             * line splitting). */
+            apr_bucket_delete(e);
+            continue;
+        }
+        count += len;
+        if (count > MAX_CONTENT) {
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01986)
+                          "OCSP response size exceeds %u byte limit",
+                          MAX_CONTENT);
+            return NULL;
+        }
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01987)
+                      "OCSP response: got %" APR_SIZE_T_FMT
+                      " bytes, %" APR_SIZE_T_FMT " total", len, count);
+
+        BIO_write(bio, data, (int)len);
+        apr_bucket_delete(e);
+    }
+
+    apr_brigade_destroy(bb);
+    apr_brigade_destroy(tmpbb);
+
+    /* Finally decode the OCSP response from what's stored in the
+     * bio. */
+    response = d2i_OCSP_RESPONSE_bio(bio, NULL);
+    if (response == NULL) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01988)
+                      "failed to decode OCSP response data");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, mySrvFromConn(c));
+    }
+
+    return response;
+}
+
+OCSP_RESPONSE *modssl_dispatch_ocsp_request(const apr_uri_t *uri,
+                                            apr_interval_time_t timeout,
+                                            OCSP_REQUEST *request,
+                                            conn_rec *c, apr_pool_t *p)
+{
+    OCSP_RESPONSE *response = NULL;
+    apr_socket_t *sd;
+    BIO *bio;
+
+    bio = serialize_request(request, uri);
+    if (bio == NULL) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01989)
+                      "could not serialize OCSP request");
+        ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, mySrvFromConn(c));
+        return NULL;
+    }
+
+    sd = send_request(bio, uri, timeout, c, p);
+    if (sd == NULL) {
+        /* Errors already logged. */
+        BIO_free(bio);
+        return NULL;
+    }
+
+    /* Clear the BIO contents, ready for the response. */
+    (void)BIO_reset(bio);
+
+    response = read_response(sd, bio, c, p);
+
+    apr_socket_close(sd);
+    BIO_free(bio);
+
+    return response;
+}
+
+#endif /* HAVE_OCSP */
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ssl.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ssl.c
new file mode 100644 (file)
index 0000000..0bf3776
--- /dev/null
@@ -0,0 +1,537 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_util_ssl.c
+ *  Additional Utility Functions for OpenSSL
+ */
+
+#include "ssl_private.h"
+
+/*  _________________________________________________________________
+**
+**  Additional High-Level Functions for OpenSSL
+**  _________________________________________________________________
+*/
+
+/* we initialize this index at startup time
+ * and never write to it at request time,
+ * so this static is thread safe.
+ * also note that OpenSSL increments at static variable when
+ * SSL_get_ex_new_index() is called, so we _must_ do this at startup.
+ */
+static int SSL_app_data2_idx = -1;
+
+void SSL_init_app_data2_idx(void)
+{
+    int i;
+
+    if (SSL_app_data2_idx > -1) {
+        return;
+    }
+
+    /* we _do_ need to call this twice */
+    for (i=0; i<=1; i++) {
+        SSL_app_data2_idx =
+            SSL_get_ex_new_index(0,
+                                 "Second Application Data for SSL",
+                                 NULL, NULL, NULL);
+    }
+}
+
+void *SSL_get_app_data2(SSL *ssl)
+{
+    return (void *)SSL_get_ex_data(ssl, SSL_app_data2_idx);
+}
+
+void SSL_set_app_data2(SSL *ssl, void *arg)
+{
+    SSL_set_ex_data(ssl, SSL_app_data2_idx, (char *)arg);
+    return;
+}
+
+/*  _________________________________________________________________
+**
+**  High-Level Private Key Loading
+**  _________________________________________________________________
+*/
+
+EVP_PKEY *SSL_read_PrivateKey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
+{
+    EVP_PKEY *rc;
+    BIO *bioS;
+    BIO *bioF;
+
+    /* 1. try PEM (= DER+Base64+headers) */
+    if ((bioS=BIO_new_file(filename, "r")) == NULL)
+        return NULL;
+    rc = PEM_read_bio_PrivateKey(bioS, key, cb, s);
+    BIO_free(bioS);
+
+    if (rc == NULL) {
+        /* 2. try DER+Base64 */
+        if ((bioS = BIO_new_file(filename, "r")) == NULL)
+            return NULL;
+
+        if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
+            BIO_free(bioS);
+            return NULL;
+        }
+        bioS = BIO_push(bioF, bioS);
+        rc = d2i_PrivateKey_bio(bioS, NULL);
+        BIO_free_all(bioS);
+
+        if (rc == NULL) {
+            /* 3. try plain DER */
+            if ((bioS = BIO_new_file(filename, "r")) == NULL)
+                return NULL;
+            rc = d2i_PrivateKey_bio(bioS, NULL);
+            BIO_free(bioS);
+        }
+    }
+    if (rc != NULL && key != NULL) {
+        if (*key != NULL)
+            EVP_PKEY_free(*key);
+        *key = rc;
+    }
+    return rc;
+}
+
+/*  _________________________________________________________________
+**
+**  Smart shutdown
+**  _________________________________________________________________
+*/
+
+int SSL_smart_shutdown(SSL *ssl)
+{
+    int i;
+    int rc;
+
+    /*
+     * Repeat the calls, because SSL_shutdown internally dispatches through a
+     * little state machine. Usually only one or two interation should be
+     * needed, so we restrict the total number of restrictions in order to
+     * avoid process hangs in case the client played bad with the socket
+     * connection and OpenSSL cannot recognize it.
+     */
+    rc = 0;
+    for (i = 0; i < 4 /* max 2x pending + 2x data = 4 */; i++) {
+        if ((rc = SSL_shutdown(ssl)))
+            break;
+    }
+    return rc;
+}
+
+/*  _________________________________________________________________
+**
+**  Certificate Checks
+**  _________________________________________________________________
+*/
+
+/* retrieve basic constraints ingredients */
+BOOL SSL_X509_getBC(X509 *cert, int *ca, int *pathlen)
+{
+    BASIC_CONSTRAINTS *bc;
+    BIGNUM *bn = NULL;
+    char *cp;
+
+    bc = X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL);
+    if (bc == NULL)
+        return FALSE;
+    *ca = bc->ca;
+    *pathlen = -1 /* unlimited */;
+    if (bc->pathlen != NULL) {
+        if ((bn = ASN1_INTEGER_to_BN(bc->pathlen, NULL)) == NULL)
+            return FALSE;
+        if ((cp = BN_bn2dec(bn)) == NULL)
+            return FALSE;
+        *pathlen = atoi(cp);
+        free(cp);
+        BN_free(bn);
+    }
+    BASIC_CONSTRAINTS_free(bc);
+    return TRUE;
+}
+
+/* convert a NAME_ENTRY to UTF8 string */
+char *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne)
+{
+    char *result = NULL;
+    BIO* bio;
+    int len;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    ASN1_STRING_print_ex(bio, X509_NAME_ENTRY_get_data(xsne),
+                         ASN1_STRFLGS_ESC_CTRL|ASN1_STRFLGS_UTF8_CONVERT);
+    len = BIO_pending(bio);
+    result = apr_palloc(p, len+1);
+    len = BIO_read(bio, result, len);
+    result[len] = NUL;
+    BIO_free(bio);
+    ap_xlate_proto_from_ascii(result, len);
+    return result;
+}
+
+/*
+ * convert an X509_NAME to an RFC 2253 formatted string, optionally truncated
+ * to maxlen characters (specify a maxlen of 0 for no length limit)
+ */
+char *SSL_X509_NAME_to_string(apr_pool_t *p, X509_NAME *dn, int maxlen)
+{
+    char *result = NULL;
+    BIO *bio;
+    int len;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    X509_NAME_print_ex(bio, dn, 0, XN_FLAG_RFC2253);
+    len = BIO_pending(bio);
+    if (len > 0) {
+        result = apr_palloc(p, (maxlen > 0) ? maxlen+1 : len+1);
+        if (maxlen > 0 && maxlen < len) {
+            len = BIO_read(bio, result, maxlen);
+            if (maxlen > 2) {
+                /* insert trailing ellipsis if there's enough space */
+                apr_snprintf(result + maxlen - 3, 4, "...");
+            }
+        } else {
+            len = BIO_read(bio, result, len);
+        }
+        result[len] = NUL;
+    }
+    BIO_free(bio);
+
+    return result;
+}
+
+/* return an array of (RFC 6125 coined) DNS-IDs and CN-IDs in a certificate */
+BOOL SSL_X509_getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids)
+{
+    STACK_OF(GENERAL_NAME) *names;
+    BIO *bio;
+    X509_NAME *subj;
+    char **cpp;
+    int i, n;
+
+    if (!x509 || !(*ids = apr_array_make(p, 0, sizeof(char *)))) {
+        *ids = NULL;
+        return FALSE;
+    }
+
+    /* First, the DNS-IDs (dNSName entries in the subjectAltName extension) */
+    if ((names = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL)) &&
+        (bio = BIO_new(BIO_s_mem()))) {
+        GENERAL_NAME *name;
+
+        for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
+            name = sk_GENERAL_NAME_value(names, i);
+            if (name->type == GEN_DNS) {
+                ASN1_STRING_print_ex(bio, name->d.ia5, ASN1_STRFLGS_ESC_CTRL|
+                                     ASN1_STRFLGS_UTF8_CONVERT);
+                n = BIO_pending(bio);
+                if (n > 0) {
+                    cpp = (char **)apr_array_push(*ids);
+                    *cpp = apr_palloc(p, n+1);
+                    n = BIO_read(bio, *cpp, n);
+                    (*cpp)[n] = NUL;
+                }
+            }
+        }
+        BIO_free(bio);
+    }
+
+    if (names)
+        sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+
+    /* Second, the CN-IDs (commonName attributes in the subject DN) */
+    subj = X509_get_subject_name(x509);
+    i = -1;
+    while ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) != -1) {
+        cpp = (char **)apr_array_push(*ids);
+        *cpp = SSL_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i));
+    }
+
+    return apr_is_empty_array(*ids) ? FALSE : TRUE;
+}
+
+/* 
+ * Check if a certificate matches for a particular name, by iterating over its
+ * DNS-IDs and CN-IDs (RFC 6125), optionally with basic wildcard matching.
+ * If server_rec is non-NULL, some (debug/trace) logging is enabled.
+ */
+BOOL SSL_X509_match_name(apr_pool_t *p, X509 *x509, const char *name,
+                         BOOL allow_wildcard, server_rec *s)
+{
+    BOOL matched = FALSE;
+    apr_array_header_t *ids;
+
+    /*
+     * At some day in the future, this might be replaced with X509_check_host()
+     * (available in OpenSSL 1.0.2 and later), but two points should be noted:
+     * 1) wildcard matching in X509_check_host() might yield different
+     *    results (by default, it supports a broader set of patterns, e.g.
+     *    wildcards in non-initial positions);
+     * 2) we lose the option of logging each DNS- and CN-ID (until a match
+     *    is found).
+     */
+
+    if (SSL_X509_getIDs(p, x509, &ids)) {
+        const char *cp;
+        int i;
+        char **id = (char **)ids->elts;
+        BOOL is_wildcard;
+
+        for (i = 0; i < ids->nelts; i++) {
+            if (!id[i])
+                continue;
+
+            /*
+             * Determine if it is a wildcard ID - we're restrictive
+             * in the sense that we require the wildcard character to be
+             * THE left-most label (i.e., the ID must start with "*.")
+             */
+            is_wildcard = (*id[i] == '*' && *(id[i]+1) == '.') ? TRUE : FALSE;
+
+            /*
+             * If the ID includes a wildcard character (and the caller is
+             * allowing wildcards), check if it matches for the left-most
+             * DNS label - i.e., the wildcard character is not allowed
+             * to match a dot. Otherwise, try a simple string compare.
+             */
+            if ((allow_wildcard == TRUE && is_wildcard == TRUE &&
+                 (cp = ap_strchr_c(name, '.')) && !strcasecmp(id[i]+1, cp)) ||
+                !strcasecmp(id[i], name)) {
+                matched = TRUE;
+            }
+
+            if (s) {
+                ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
+                             "[%s] SSL_X509_match_name: expecting name '%s', "
+                             "%smatched by ID '%s'",
+                             (mySrvConfig(s))->vhost_id, name,
+                             matched == TRUE ? "" : "NOT ", id[i]);
+            }
+
+            if (matched == TRUE) {
+                break;
+            }
+        }
+
+    }
+
+    if (s) {
+        ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, p, s, x509,
+                       APLOGNO(02412) "[%s] Cert %s for name '%s'",
+                       (mySrvConfig(s))->vhost_id,
+                       matched == TRUE ? "matches" : "does not match",
+                       name);
+    }
+
+    return matched;
+}
+
+/*  _________________________________________________________________
+**
+**  Low-Level CA Certificate Loading
+**  _________________________________________________________________
+*/
+
+BOOL SSL_X509_INFO_load_file(apr_pool_t *ptemp,
+                             STACK_OF(X509_INFO) *sk,
+                             const char *filename)
+{
+    BIO *in;
+
+    if (!(in = BIO_new(BIO_s_file()))) {
+        return FALSE;
+    }
+
+    if (BIO_read_filename(in, filename) <= 0) {
+        BIO_free(in);
+        return FALSE;
+    }
+
+    ERR_clear_error();
+
+    PEM_X509_INFO_read_bio(in, sk, NULL, NULL);
+
+    BIO_free(in);
+
+    return TRUE;
+}
+
+BOOL SSL_X509_INFO_load_path(apr_pool_t *ptemp,
+                             STACK_OF(X509_INFO) *sk,
+                             const char *pathname)
+{
+    /* XXX: this dir read code is exactly the same as that in
+     * ssl_engine_init.c, only the call to handle the fullname is different,
+     * should fold the duplication.
+     */
+    apr_dir_t *dir;
+    apr_finfo_t dirent;
+    apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
+    const char *fullname;
+    BOOL ok = FALSE;
+
+    if (apr_dir_open(&dir, pathname, ptemp) != APR_SUCCESS) {
+        return FALSE;
+    }
+
+    while ((apr_dir_read(&dirent, finfo_flags, dir)) == APR_SUCCESS) {
+        if (dirent.filetype == APR_DIR) {
+            continue; /* don't try to load directories */
+        }
+
+        fullname = apr_pstrcat(ptemp,
+                               pathname, "/", dirent.name,
+                               NULL);
+
+        if (SSL_X509_INFO_load_file(ptemp, sk, fullname)) {
+            ok = TRUE;
+        }
+    }
+
+    apr_dir_close(dir);
+
+    return ok;
+}
+
+/*  _________________________________________________________________
+**
+**  Custom (EC)DH parameter support
+**  _________________________________________________________________
+*/
+
+DH *ssl_dh_GetParamFromFile(const char *file)
+{
+    DH *dh = NULL;
+    BIO *bio;
+
+    if ((bio = BIO_new_file(file, "r")) == NULL)
+        return NULL;
+    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+    BIO_free(bio);
+    return (dh);
+}
+
+#ifdef HAVE_ECC
+EC_GROUP *ssl_ec_GetParamFromFile(const char *file)
+{
+    EC_GROUP *group = NULL;
+    BIO *bio;
+
+    if ((bio = BIO_new_file(file, "r")) == NULL)
+        return NULL;
+    group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL);
+    BIO_free(bio);
+    return (group);
+}
+#endif
+
+/*  _________________________________________________________________
+**
+**  Extra Server Certificate Chain Support
+**  _________________________________________________________________
+*/
+
+/*
+ * Read a file that optionally contains the server certificate in PEM
+ * format, possibly followed by a sequence of CA certificates that
+ * should be sent to the peer in the SSL Certificate message.
+ */
+int SSL_CTX_use_certificate_chain(
+    SSL_CTX *ctx, char *file, int skipfirst, pem_password_cb *cb)
+{
+    BIO *bio;
+    X509 *x509;
+    unsigned long err;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_file_internal())) == NULL)
+        return -1;
+    if (BIO_read_filename(bio, file) <= 0) {
+        BIO_free(bio);
+        return -1;
+    }
+    /* optionally skip a leading server certificate */
+    if (skipfirst) {
+        if ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) == NULL) {
+            BIO_free(bio);
+            return -1;
+        }
+        X509_free(x509);
+    }
+    /* free a perhaps already configured extra chain */
+#ifdef OPENSSL_NO_SSL_INTERN
+    SSL_CTX_clear_extra_chain_certs(ctx);
+#else
+    if (ctx->extra_certs != NULL) {
+        sk_X509_pop_free((STACK_OF(X509) *)ctx->extra_certs, X509_free);
+        ctx->extra_certs = NULL;
+    }
+#endif
+    /* create new extra chain by loading the certs */
+    n = 0;
+    while ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) != NULL) {
+        if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) {
+            X509_free(x509);
+            BIO_free(bio);
+            return -1;
+        }
+        n++;
+    }
+    /* Make sure that only the error is just an EOF */
+    if ((err = ERR_peek_error()) > 0) {
+        if (!(   ERR_GET_LIB(err) == ERR_LIB_PEM
+              && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+            BIO_free(bio);
+            return -1;
+        }
+        while (ERR_get_error() > 0) ;
+    }
+    BIO_free(bio);
+    return n;
+}
+
+/*  _________________________________________________________________
+**
+**  Session Stuff
+**  _________________________________________________________________
+*/
+
+char *SSL_SESSION_id2sz(unsigned char *id, int idlen,
+                        char *str, int strsize)
+{
+    if (idlen > SSL_MAX_SSL_SESSION_ID_LENGTH)
+        idlen = SSL_MAX_SSL_SESSION_ID_LENGTH;
+        
+    /* We must ensure not to process more than what would fit in the
+     * destination buffer, including terminating NULL */
+    if (idlen > (strsize-1) / 2)
+        idlen = (strsize-1) / 2;
+
+    ap_bin2hex(id, idlen, str);
+
+    return str;
+}
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ssl.h b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_ssl.h
new file mode 100644 (file)
index 0000000..6f6873b
--- /dev/null
@@ -0,0 +1,77 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @verbatim
+                        _             _
+    _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+   | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+   | | | | | | (_) | (_| |   \__ \__ \ |
+   |_| |_| |_|\___/ \__,_|___|___/___/_|
+                        |_____|
+   @endverbatim
+ * @file  ssl_util_ssl.h
+ * @brief Additional Utility Functions for OpenSSL
+ *
+ * @defgroup MOD_SSL_UTIL Utilities
+ * @ingroup MOD_SSL
+ * @{
+ */
+
+#ifndef __SSL_UTIL_SSL_H__
+#define __SSL_UTIL_SSL_H__
+
+/**
+ * SSL library version number
+ */
+
+#define SSL_LIBRARY_VERSION OPENSSL_VERSION_NUMBER
+#define SSL_LIBRARY_NAME    "OpenSSL"
+#define SSL_LIBRARY_TEXT    OPENSSL_VERSION_TEXT
+#define SSL_LIBRARY_DYNTEXT SSLeay_version(SSLEAY_VERSION)
+
+/**
+ *  Maximum length of a DER encoded session.
+ *  FIXME: There is no define in OpenSSL, but OpenSSL uses 1024*10,
+ *         so this value should be ok. Although we have no warm feeling.
+ */
+#define SSL_SESSION_MAX_DER 1024*10
+
+/** max length for SSL_SESSION_id2sz */
+#define SSL_SESSION_ID_STRING_LEN \
+    ((SSL_MAX_SSL_SESSION_ID_LENGTH + 1) * 2)
+
+/**
+ *  Additional Functions
+ */
+void        SSL_init_app_data2_idx(void);
+void       *SSL_get_app_data2(SSL *);
+void        SSL_set_app_data2(SSL *, void *);
+EVP_PKEY   *SSL_read_PrivateKey(const char *, EVP_PKEY **, pem_password_cb *, void *);
+int         SSL_smart_shutdown(SSL *ssl);
+BOOL        SSL_X509_getBC(X509 *, int *, int *);
+char       *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne);
+char       *SSL_X509_NAME_to_string(apr_pool_t *, X509_NAME *, int);
+BOOL        SSL_X509_getIDs(apr_pool_t *, X509 *, apr_array_header_t **);
+BOOL        SSL_X509_match_name(apr_pool_t *, X509 *, const char *, BOOL, server_rec *);
+BOOL        SSL_X509_INFO_load_file(apr_pool_t *, STACK_OF(X509_INFO) *, const char *);
+BOOL        SSL_X509_INFO_load_path(apr_pool_t *, STACK_OF(X509_INFO) *, const char *);
+int         SSL_CTX_use_certificate_chain(SSL_CTX *, char *, int, pem_password_cb *);
+char       *SSL_SESSION_id2sz(unsigned char *, int, char *, int);
+
+#endif /* __SSL_UTIL_SSL_H__ */
+/** @} */
+
diff --git a/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_stapling.c b/modules/http2/sandbox/httpd/mod_ssl-alpn/ssl_util_stapling.c
new file mode 100644 (file)
index 0000000..2dc8fce
--- /dev/null
@@ -0,0 +1,705 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*                      _             _
+ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *                      |_____|
+ *  ssl_stapling.c
+ *  OCSP Stapling Support
+ */
+                             /* ``Where's the spoons?
+                                  Where's the spoons?
+                                  Where's the bloody spoons?''
+                                            -- Alexei Sayle          */
+
+#include "ssl_private.h"
+#include "ap_mpm.h"
+#include "apr_thread_mutex.h"
+
+#ifdef HAVE_OCSP_STAPLING
+
+/**
+ * Maxiumum OCSP stapling response size. This should be the response for a
+ * single certificate and will typically include the responder certificate chain
+ * so 10K should be more than enough.
+ *
+ */
+
+#define MAX_STAPLING_DER 10240
+
+/* Cached info stored in certificate ex_info. */
+typedef struct {
+    /* Index in session cache SHA1 hash of certificate */
+    UCHAR idx[20];
+    /* Certificate ID for OCSP requests or NULL if ID cannot be determined */
+    OCSP_CERTID *cid;
+    /* Responder details */
+    char *uri;
+} certinfo;
+
+static void certinfo_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+                                        int idx, long argl, void *argp)
+{
+    certinfo *cinf = ptr;
+
+    if (!cinf)
+        return;
+    if (cinf->uri)
+        OPENSSL_free(cinf->uri);
+    OPENSSL_free(cinf);
+}
+
+static int stapling_ex_idx = -1;
+
+void ssl_stapling_ex_init(void)
+{
+    if (stapling_ex_idx != -1)
+        return;
+    stapling_ex_idx = X509_get_ex_new_index(0, "X509 cached OCSP info", 0, 0,
+                                            certinfo_free);
+}
+
+static X509 *stapling_get_issuer(modssl_ctx_t *mctx, X509 *x)
+{
+    X509 *issuer = NULL;
+    int i;
+    X509_STORE *st = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+    X509_STORE_CTX inctx;
+    STACK_OF(X509) *extra_certs = NULL;
+
+#ifdef OPENSSL_NO_SSL_INTERN
+    SSL_CTX_get_extra_chain_certs(mctx->ssl_ctx, &extra_certs);
+#else
+    extra_certs = mctx->ssl_ctx->extra_certs;
+#endif
+
+    for (i = 0; i < sk_X509_num(extra_certs); i++) {
+        issuer = sk_X509_value(extra_certs, i);
+        if (X509_check_issued(issuer, x) == X509_V_OK) {
+            CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);
+            return issuer;
+        }
+    }
+
+    if (!X509_STORE_CTX_init(&inctx, st, NULL, NULL))
+        return 0;
+    if (X509_STORE_CTX_get1_issuer(&issuer, &inctx, x) <= 0)
+        issuer = NULL;
+    X509_STORE_CTX_cleanup(&inctx);
+    return issuer;
+
+}
+
+int ssl_stapling_init_cert(server_rec *s, modssl_ctx_t *mctx, X509 *x)
+{
+    certinfo *cinf;
+    X509 *issuer = NULL;
+    STACK_OF(OPENSSL_STRING) *aia = NULL;
+
+    if (x == NULL)
+        return 0;
+    cinf  = X509_get_ex_data(x, stapling_ex_idx);
+    if (cinf) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02215)
+                     "ssl_stapling_init_cert: certificate already initialized!");
+        return 0;
+    }
+    cinf = OPENSSL_malloc(sizeof(certinfo));
+    if (!cinf) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02216)
+                     "ssl_stapling_init_cert: error allocating memory!");
+        return 0;
+    }
+    cinf->cid = NULL;
+    cinf->uri = NULL;
+    X509_set_ex_data(x, stapling_ex_idx, cinf);
+
+    issuer = stapling_get_issuer(mctx, x);
+
+    if (issuer == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02217)
+                     "ssl_stapling_init_cert: Can't retrieve issuer certificate!");
+        return 0;
+    }
+
+    cinf->cid = OCSP_cert_to_id(NULL, x, issuer);
+    X509_free(issuer);
+    if (!cinf->cid)
+        return 0;
+    X509_digest(x, EVP_sha1(), cinf->idx, NULL);
+
+    aia = X509_get1_ocsp(x);
+    if (aia) {
+        cinf->uri = sk_OPENSSL_STRING_pop(aia);
+        X509_email_free(aia);
+    }
+    if (!cinf->uri && !mctx->stapling_force_url) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02218)
+                     "ssl_stapling_init_cert: no responder URL");
+        return 0;
+    }
+    return 1;
+}
+
+static certinfo *stapling_get_cert_info(server_rec *s, modssl_ctx_t *mctx,
+                                        SSL *ssl)
+{
+    certinfo *cinf;
+    X509 *x;
+    x = SSL_get_certificate(ssl);
+    if (x == NULL)
+        return NULL;
+    cinf = X509_get_ex_data(x, stapling_ex_idx);
+    if (cinf && cinf->cid)
+        return cinf;
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01926)
+                 "stapling_get_cert_info: stapling not supported for certificate");
+    return NULL;
+}
+
+/*
+ * OCSP response caching code. The response is preceded by a flag value
+ * which indicates whether the response was invalid when it was stored.
+ * the purpose of this flag is to avoid repeated queries to a server
+ * which has given an invalid response while allowing a response which
+ * has subsequently become invalid to be retried immediately.
+ *
+ * The key for the cache is the hash of the certificate the response
+ * is for.
+ */
+static BOOL stapling_cache_response(server_rec *s, modssl_ctx_t *mctx,
+                                    OCSP_RESPONSE *rsp, certinfo *cinf,
+                                    BOOL ok, apr_pool_t *pool)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    unsigned char resp_der[MAX_STAPLING_DER];
+    unsigned char *p;
+    int resp_derlen;
+    BOOL rv;
+    apr_time_t expiry;
+
+    resp_derlen = i2d_OCSP_RESPONSE(rsp, NULL) + 1;
+
+    if (resp_derlen <= 0) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01927)
+                     "OCSP stapling response encode error??");
+        return FALSE;
+    }
+
+    if (resp_derlen > sizeof resp_der) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01928)
+                     "OCSP stapling response too big (%u bytes)", resp_derlen);
+        return FALSE;
+    }
+
+    p = resp_der;
+
+    /* TODO: potential optimization; _timeout members as apr_interval_time_t */
+    if (ok == TRUE) {
+        *p++ = 1;
+        expiry = apr_time_from_sec(mctx->stapling_cache_timeout);
+    }
+    else {
+        *p++ = 0;
+        expiry = apr_time_from_sec(mctx->stapling_errcache_timeout);
+    }
+
+    expiry += apr_time_now();
+
+    i2d_OCSP_RESPONSE(rsp, &p);
+
+    rv = mc->stapling_cache->store(mc->stapling_cache_context, s,
+                                   cinf->idx, sizeof(cinf->idx),
+                                   expiry, resp_der, resp_derlen, pool);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01929)
+                     "stapling_cache_response: OCSP response session store error!");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static BOOL stapling_get_cached_response(server_rec *s, OCSP_RESPONSE **prsp,
+                                         BOOL *pok, certinfo *cinf,
+                                         apr_pool_t *pool)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+    OCSP_RESPONSE *rsp;
+    unsigned char resp_der[MAX_STAPLING_DER];
+    const unsigned char *p;
+    unsigned int resp_derlen = MAX_STAPLING_DER;
+
+    rv = mc->stapling_cache->retrieve(mc->stapling_cache_context, s,
+                                      cinf->idx, sizeof(cinf->idx),
+                                      resp_der, &resp_derlen, pool);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01930)
+                     "stapling_get_cached_response: cache miss");
+        return TRUE;
+    }
+    if (resp_derlen <= 1) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01931)
+                     "stapling_get_cached_response: response length invalid??");
+        return TRUE;
+    }
+    p = resp_der;
+    if (pok) {
+        if (*p)
+            *pok = TRUE;
+        else
+            *pok = FALSE;
+    }
+    p++;
+    resp_derlen--;
+    rsp = d2i_OCSP_RESPONSE(NULL, &p, resp_derlen);
+    if (!rsp) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01932)
+                     "stapling_get_cached_response: response parse error??");
+        return TRUE;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01933)
+                 "stapling_get_cached_response: cache hit");
+
+    *prsp = rsp;
+
+    return TRUE;
+}
+
+static int stapling_set_response(SSL *ssl, OCSP_RESPONSE *rsp)
+{
+    int rspderlen;
+    unsigned char *rspder = NULL;
+
+    rspderlen = i2d_OCSP_RESPONSE(rsp, &rspder);
+    if (rspderlen <= 0)
+        return 0;
+    SSL_set_tlsext_status_ocsp_resp(ssl, rspder, rspderlen);
+    return 1;
+}
+
+static int stapling_check_response(server_rec *s, modssl_ctx_t *mctx,
+                                   certinfo *cinf, OCSP_RESPONSE *rsp,
+                                   BOOL *pok)
+{
+    int status, reason;
+    OCSP_BASICRESP *bs = NULL;
+    ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+    int response_status = OCSP_response_status(rsp);
+
+    if (pok)
+        *pok = FALSE;
+    /* Check to see if response is an error. If so we automatically accept
+     * it because it would have expired from the cache if it was time to
+     * retry.
+     */
+    if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+        if (mctx->stapling_return_errors)
+            return SSL_TLSEXT_ERR_OK;
+        else
+            return SSL_TLSEXT_ERR_NOACK;
+    }
+
+    bs = OCSP_response_get1_basic(rsp);
+    if (bs == NULL) {
+        /* If we can't parse response just pass it to client */
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01934)
+                     "stapling_check_response: Error Parsing Response!");
+        return SSL_TLSEXT_ERR_OK;
+    }
+
+    if (!OCSP_resp_find_status(bs, cinf->cid, &status, &reason, &rev,
+                               &thisupd, &nextupd)) {
+        /* If ID not present just pass back to client */
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01935)
+                     "stapling_check_response: certificate ID not present in response!");
+    }
+    else {
+        if (OCSP_check_validity(thisupd, nextupd,
+                                mctx->stapling_resptime_skew,
+                                mctx->stapling_resp_maxage)) {
+            if (pok)
+                *pok = TRUE;
+        }
+        else {
+            /* If pok is not NULL response was direct from a responder and
+             * the times should be valide. If pok is NULL the response was
+             * retrieved from cache and it is expected to subsequently expire
+             */
+            if (pok) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01936)
+                             "stapling_check_response: response times invalid");
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01937)
+                             "stapling_check_response: cached response expired");
+            }
+
+            OCSP_BASICRESP_free(bs);
+            return SSL_TLSEXT_ERR_NOACK;
+        }
+    }
+
+    OCSP_BASICRESP_free(bs);
+
+    return SSL_TLSEXT_ERR_OK;
+}
+
+static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl,
+                                    certinfo *cinf, OCSP_RESPONSE **prsp,
+                                    apr_pool_t *pool)
+{
+    conn_rec *conn      = (conn_rec *)SSL_get_app_data(ssl);
+    apr_pool_t *vpool;
+    OCSP_REQUEST *req = NULL;
+    OCSP_CERTID *id = NULL;
+    STACK_OF(X509_EXTENSION) *exts;
+    int i;
+    BOOL ok = FALSE;
+    BOOL rv = TRUE;
+    const char *ocspuri;
+    apr_uri_t uri;
+
+    *prsp = NULL;
+    /* Build up OCSP query from server certificate info */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01938)
+                 "stapling_renew_response: querying responder");
+
+    req = OCSP_REQUEST_new();
+    if (!req)
+        goto err;
+    id = OCSP_CERTID_dup(cinf->cid);
+    if (!id)
+        goto err;
+    if (!OCSP_request_add0_id(req, id))
+        goto err;
+    id = NULL;
+    /* Add any extensions to the request */
+    SSL_get_tlsext_status_exts(ssl, &exts);
+    for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
+        X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+        if (!OCSP_REQUEST_add_ext(req, ext, -1))
+            goto err;
+    }
+
+    if (mctx->stapling_force_url)
+        ocspuri = mctx->stapling_force_url;
+    else
+        ocspuri = cinf->uri;
+
+    if (!ocspuri) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02621)
+                     "stapling_renew_response: no uri for responder");
+        rv = FALSE;
+        goto done;
+    }
+
+    /* Create a temporary pool to constrain memory use */
+    apr_pool_create(&vpool, conn->pool);
+
+    ok = apr_uri_parse(vpool, ocspuri, &uri);
+    if (ok != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01939)
+                     "stapling_renew_response: Error parsing uri %s",
+                      ocspuri);
+        rv = FALSE;
+        goto done;
+    }
+    else if (strcmp(uri.scheme, "http")) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01940)
+                     "stapling_renew_response: Unsupported uri %s", ocspuri);
+        rv = FALSE;
+        goto done;
+    }
+
+    if (!uri.port) {
+        uri.port = apr_uri_port_of_scheme(uri.scheme);
+    }
+
+    *prsp = modssl_dispatch_ocsp_request(&uri, mctx->stapling_responder_timeout,
+                                         req, conn, vpool);
+
+    apr_pool_destroy(vpool);
+
+    if (!*prsp) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01941)
+                     "stapling_renew_response: responder error");
+        if (mctx->stapling_fake_trylater) {
+            *prsp = OCSP_response_create(OCSP_RESPONSE_STATUS_TRYLATER, NULL);
+        }
+        else {
+            goto done;
+        }
+    }
+    else {
+        int response_status = OCSP_response_status(*prsp);
+
+        if (response_status == OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01942)
+                        "stapling_renew_response: query response received");
+            stapling_check_response(s, mctx, cinf, *prsp, &ok);
+            if (ok == FALSE) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01943)
+                             "stapling_renew_response: error in retrieved response!");
+            }
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01944)
+                         "stapling_renew_response: responder error %s",
+                         OCSP_response_status_str(response_status));
+        }
+    }
+    if (stapling_cache_response(s, mctx, *prsp, cinf, ok, pool) == FALSE) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01945)
+                     "stapling_renew_response: error caching response!");
+    }
+
+done:
+    if (id)
+        OCSP_CERTID_free(id);
+    if (req)
+        OCSP_REQUEST_free(req);
+    return rv;
+err:
+    rv = FALSE;
+    goto done;
+}
+
+/*
+ * SSLStaplingMutex operations. Similar to SSL mutex except a mutex is
+ * mandatory if stapling is enabled.
+ */
+static int ssl_stapling_mutex_init(server_rec *s, apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    apr_status_t rv;
+
+    if (mc->stapling_mutex || sc->server->stapling_enabled != TRUE) {
+        return TRUE;
+    }
+
+    if ((rv = ap_global_mutex_create(&mc->stapling_mutex, NULL,
+                                     SSL_STAPLING_MUTEX_TYPE, NULL, s,
+                                     s->process->pool, 0)) != APR_SUCCESS) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+int ssl_stapling_mutex_reinit(server_rec *s, apr_pool_t *p)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+    const char *lockfile;
+
+    if (mc->stapling_mutex == NULL) {
+        return TRUE;
+    }
+
+    lockfile = apr_global_mutex_lockfile(mc->stapling_mutex);
+    if ((rv = apr_global_mutex_child_init(&mc->stapling_mutex,
+                                          lockfile, p)) != APR_SUCCESS) {
+        if (lockfile) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01946)
+                         "Cannot reinit %s mutex with file `%s'",
+                         SSL_STAPLING_MUTEX_TYPE, lockfile);
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01947)
+                         "Cannot reinit %s mutex", SSL_STAPLING_MUTEX_TYPE);
+        }
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static int stapling_mutex_on(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+
+    if ((rv = apr_global_mutex_lock(mc->stapling_mutex)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01948)
+                     "Failed to acquire OCSP stapling lock");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static int stapling_mutex_off(server_rec *s)
+{
+    SSLModConfigRec *mc = myModConfig(s);
+    apr_status_t rv;
+
+    if ((rv = apr_global_mutex_unlock(mc->stapling_mutex)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01949)
+                     "Failed to release OCSP stapling lock");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/* Certificate Status callback. This is called when a client includes a
+ * certificate status request extension.
+ *
+ * Check for cached responses in session cache. If valid send back to
+ * client.  If absent or no longer valid query responder and update
+ * cache. */
+static int stapling_cb(SSL *ssl, void *arg)
+{
+    conn_rec *conn      = (conn_rec *)SSL_get_app_data(ssl);
+    server_rec *s       = mySrvFromConn(conn);
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+    SSLConnRec *sslconn = myConnConfig(conn);
+    modssl_ctx_t *mctx  = myCtxConfig(sslconn, sc);
+    certinfo *cinf = NULL;
+    OCSP_RESPONSE *rsp = NULL;
+    int rv;
+    BOOL ok;
+
+    if (sc->server->stapling_enabled != TRUE) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01950)
+                     "stapling_cb: OCSP Stapling disabled");
+        return SSL_TLSEXT_ERR_NOACK;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951)
+                 "stapling_cb: OCSP Stapling callback called");
+
+    cinf = stapling_get_cert_info(s, mctx, ssl);
+    if (cinf == NULL) {
+        return SSL_TLSEXT_ERR_NOACK;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01952)
+                 "stapling_cb: retrieved cached certificate data");
+
+    /* Check to see if we already have a response for this certificate */
+    stapling_mutex_on(s);
+
+    rv = stapling_get_cached_response(s, &rsp, &ok, cinf, conn->pool);
+    if (rv == FALSE) {
+        stapling_mutex_off(s);
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+
+    if (rsp) {
+        /* see if response is acceptable */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01953)
+                     "stapling_cb: retrieved cached response");
+        rv = stapling_check_response(s, mctx, cinf, rsp, NULL);
+        if (rv == SSL_TLSEXT_ERR_ALERT_FATAL) {
+            OCSP_RESPONSE_free(rsp);
+            stapling_mutex_off(s);
+            return SSL_TLSEXT_ERR_ALERT_FATAL;
+        }
+        else if (rv == SSL_TLSEXT_ERR_NOACK) {
+            /* Error in response. If this error was not present when it was
+             * stored (i.e. response no longer valid) then it can be
+             * renewed straight away.
+             *
+             * If the error *was* present at the time it was stored then we
+             * don't renew the response straight away we just wait for the
+             * cached response to expire.
+             */
+            if (ok) {
+                OCSP_RESPONSE_free(rsp);
+                rsp = NULL;
+            }
+            else if (!mctx->stapling_return_errors) {
+                OCSP_RESPONSE_free(rsp);
+                stapling_mutex_off(s);
+                return SSL_TLSEXT_ERR_NOACK;
+            }
+        }
+    }
+
+    if (rsp == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01954)
+                     "stapling_cb: renewing cached response");
+        rv = stapling_renew_response(s, mctx, ssl, cinf, &rsp, conn->pool);
+
+        if (rv == FALSE) {
+            stapling_mutex_off(s);
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01955)
+                         "stapling_cb: fatal error");
+            return SSL_TLSEXT_ERR_ALERT_FATAL;
+        }
+    }
+    stapling_mutex_off(s);
+
+    if (rsp) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01956)
+                     "stapling_cb: setting response");
+        if (!stapling_set_response(ssl, rsp))
+            return SSL_TLSEXT_ERR_ALERT_FATAL;
+        return SSL_TLSEXT_ERR_OK;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01957)
+                 "stapling_cb: no response available");
+
+    return SSL_TLSEXT_ERR_NOACK;
+
+}
+
+apr_status_t modssl_init_stapling(server_rec *s, apr_pool_t *p,
+                                  apr_pool_t *ptemp, modssl_ctx_t *mctx)
+{
+    SSL_CTX *ctx = mctx->ssl_ctx;
+    SSLModConfigRec *mc = myModConfig(s);
+
+    if (mc->stapling_cache == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01958)
+                     "SSLStapling: no stapling cache available");
+        return ssl_die(s);
+    }
+    if (ssl_stapling_mutex_init(s, ptemp) == FALSE) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01959)
+                     "SSLStapling: cannot initialise stapling mutex");
+        return ssl_die(s);
+    }
+    /* Set some default values for parameters if they are not set */
+    if (mctx->stapling_resptime_skew == UNSET) {
+        mctx->stapling_resptime_skew = 60 * 5;
+    }
+    if (mctx->stapling_cache_timeout == UNSET) {
+        mctx->stapling_cache_timeout = 3600;
+    }
+    if (mctx->stapling_return_errors == UNSET) {
+        mctx->stapling_return_errors = TRUE;
+    }
+    if (mctx->stapling_fake_trylater == UNSET) {
+        mctx->stapling_fake_trylater = TRUE;
+    }
+    if (mctx->stapling_errcache_timeout == UNSET) {
+        mctx->stapling_errcache_timeout = 600;
+    }
+    if (mctx->stapling_responder_timeout == UNSET) {
+        mctx->stapling_responder_timeout = 10 * APR_USEC_PER_SEC;
+    }
+    SSL_CTX_set_tlsext_status_cb(ctx, stapling_cb);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01960) "OCSP stapling initialized");
+
+    return APR_SUCCESS;
+}
+
+#endif
diff --git a/modules/http2/sandbox/httpd/packages/pcre-8.36.tar.gz b/modules/http2/sandbox/httpd/packages/pcre-8.36.tar.gz
new file mode 100644 (file)
index 0000000..191d855
Binary files /dev/null and b/modules/http2/sandbox/httpd/packages/pcre-8.36.tar.gz differ
diff --git a/modules/http2/sandbox/httpd/patches/httpd-2.4.12-alpn-v5.patch b/modules/http2/sandbox/httpd/patches/httpd-2.4.12-alpn-v5.patch
new file mode 100644 (file)
index 0000000..fec30a8
--- /dev/null
@@ -0,0 +1,605 @@
+diff -ru modules/ssl/mod_ssl.c modules/ssl/mod_ssl.c
+--- modules/ssl/mod_ssl.c      2015-06-17 15:09:03.000000000 +0200
++++ modules/ssl/mod_ssl.c      2015-06-17 15:52:41.000000000 +0200
+@@ -273,6 +273,12 @@
+               "OpenSSL configuration command")
+ #endif
++#ifdef HAVE_TLS_ALPN
++    SSL_CMD_SRV(ALPNPreference, ITERATE,
++                "Preference in Application-Layer Protocol Negotiation (ALPN), "
++                "protocols are chosen in the specified order")
++#endif
++
+     /* Deprecated directives. */
+     AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
+       "SSLLog directive is no longer supported - use ErrorLog."),
+@@ -372,6 +378,7 @@
+     sslconn = apr_pcalloc(c->pool, sizeof(*sslconn));
+     sslconn->server = c->base_server;
++    sslconn->vhost_selected = 0;
+     sslconn->verify_depth = UNSET;
+     myConnConfigSet(c, sslconn);
+@@ -423,12 +430,44 @@
+     return 1;
+ }
++static int modssl_register_alpn(conn_rec *c,
++                               ssl_alpn_propose_protos advertisefn,
++                               ssl_alpn_proto_negotiated negotiatedfn)
++{
++#ifdef HAVE_TLS_ALPN
++    SSLConnRec *sslconn = myConnConfig(c);
++    
++    if (!sslconn) {
++        return DECLINED;
++    }
++    
++    if (!sslconn->alpn_proposefns) {
++        sslconn->alpn_proposefns =
++        apr_array_make(c->pool, 5, sizeof(ssl_alpn_propose_protos));
++        sslconn->alpn_negofns =
++        apr_array_make(c->pool, 5, sizeof(ssl_alpn_proto_negotiated));
++    }
++    
++    if (advertisefn)
++        APR_ARRAY_PUSH(sslconn->alpn_proposefns, ssl_alpn_propose_protos) =
++            advertisefn;
++    if (negotiatedfn)
++        APR_ARRAY_PUSH(sslconn->alpn_negofns, ssl_alpn_proto_negotiated) =
++            negotiatedfn;
++    
++    return OK;
++#else
++    return DECLINED;
++#endif
++}
++
+ int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
+ {
+     SSLSrvConfigRec *sc;
+     SSL *ssl;
+     SSLConnRec *sslconn = myConnConfig(c);
+     char *vhost_md5;
++    int rc;
+     modssl_ctx_t *mctx;
+     server_rec *server;
+@@ -585,6 +624,7 @@
+     APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
+     APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
++    APR_REGISTER_OPTIONAL_FN(modssl_register_alpn);
+     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
+                               AUTHZ_PROVIDER_VERSION,
+diff -ru modules/ssl/mod_ssl.h modules/ssl/mod_ssl.h
+--- modules/ssl/mod_ssl.h      2015-06-17 15:09:03.000000000 +0200
++++ modules/ssl/mod_ssl.h      2015-06-17 15:10:39.000000000 +0200
+@@ -63,5 +63,46 @@
+ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
++/** The alpn_propose_proto callback allows other modules to propose
++ * the name of the protocol that will be chosen during the
++ * Application-Layer Protocol Negotiation (ALPN) portion of the SSL handshake.
++ * The callback is given the connection and a list of NULL-terminated
++ * protocol strings as supported by the client.  If this client_protos is 
++ * non-empty, it must pick its preferred protocol from that list. Otherwise
++ * it should add its supported protocols in order of precedence.
++ * The callback should not yet modify the connection or install any filters
++ * as its proposal(s) may be overridden by another callback or server 
++ * configuration. 
++ * It should return OK or, to prevent further processing of (other modules') 
++ * callbacks, return DONE.
++ */
++typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
++                                    apr_array_header_t *client_protos,
++                                    apr_array_header_t *proposed_protos);
++
++/** The alpn_proto_negotiated callback allows other modules to discover
++ * the name of the protocol that was chosen during the Application-Layer
++ * Protocol Negotiation (ALPN) portion of the SSL handshake.  
++ * The callback is given the connection, a
++ * non-NUL-terminated string containing the protocol name, and the
++ * length of the string; it should do something appropriate
++ * (i.e. insert or remove filters) and return OK. To prevent further
++ * processing of (other modules') callbacks, return DONE. */
++typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
++                                        const char *proto_name,
++                                        apr_size_t proto_name_len);
++
++/* An optional function which can be used to register a pair of callbacks 
++ * for ALPN handling.
++ * This optional function should be invoked from a pre_connection hook 
++ * which runs *after* mod_ssl.c's pre_connection hook.  The function returns 
++ * OK if the callbacks are registered, or DECLINED otherwise (for example if 
++ * mod_ssl does not support ALPN).
++ */
++APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
++                        (conn_rec *conn,
++                         ssl_alpn_propose_protos proposefn,
++                         ssl_alpn_proto_negotiated negotiatedfn));
++
+ #endif /* __MOD_SSL_H__ */
+ /** @} */
+diff -ru modules/ssl/ssl_engine_config.c modules/ssl/ssl_engine_config.c
+--- modules/ssl/ssl_engine_config.c    2015-06-17 15:09:03.000000000 +0200
++++ modules/ssl/ssl_engine_config.c    2015-06-17 15:10:39.000000000 +0200
+@@ -159,6 +159,9 @@
+     SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
+     mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
+ #endif
++#ifdef HAVE_TLS_ALPN
++    mctx->ssl_alpn_pref = apr_array_make(p, 5, sizeof(const char *));
++#endif
+ }
+ static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc,
+@@ -298,6 +301,9 @@
+ #ifdef HAVE_SSL_CONF_CMD
+     cfgMergeArray(ssl_ctx_param);
+ #endif
++#ifdef HAVE_TLS_ALPN
++    cfgMergeArray(ssl_alpn_pref);
++#endif
+ }
+ static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
+@@ -1869,6 +1875,16 @@
+ }
+ #endif
++#ifdef HAVE_TLS_ALPN
++const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg,
++                                      const char *protocol)
++{
++    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
++    APR_ARRAY_PUSH(sc->server->ssl_alpn_pref, const char *) = protocol;
++    return NULL;
++}
++#endif
++
+ #ifdef HAVE_SRP
+ const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
+diff -ru modules/ssl/ssl_engine_init.c modules/ssl/ssl_engine_init.c
+--- modules/ssl/ssl_engine_init.c      2015-06-17 15:09:03.000000000 +0200
++++ modules/ssl/ssl_engine_init.c      2015-06-17 15:10:39.000000000 +0200
+@@ -623,6 +623,11 @@
+     SSL_CTX_set_tmp_dh_callback(ctx,  ssl_callback_TmpDH);
+     SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
++
++#ifdef HAVE_TLS_ALPN
++    SSL_CTX_set_alpn_select_cb(
++       ctx, ssl_callback_alpn_select, NULL);
++#endif
+ }
+ static apr_status_t ssl_init_ctx_verify(server_rec *s,
+diff -ru modules/ssl/ssl_engine_io.c modules/ssl/ssl_engine_io.c
+--- modules/ssl/ssl_engine_io.c        2015-06-17 15:09:03.000000000 +0200
++++ modules/ssl/ssl_engine_io.c        2015-06-17 15:10:39.000000000 +0200
+@@ -28,6 +28,7 @@
+                                   core keeps dumping.''
+                                             -- Unknown    */
+ #include "ssl_private.h"
++#include "mod_ssl.h"
+ #include "apr_date.h"
+ /*  _________________________________________________________________
+@@ -297,6 +298,9 @@
+     apr_pool_t *pool;
+     char buffer[AP_IOBUFSIZE];
+     ssl_filter_ctx_t *filter_ctx;
++#ifdef HAVE_TLS_ALPN
++    int alpn_finished;  /* 1 if ALPN has finished, 0 otherwise */
++#endif
+ } bio_filter_in_ctx_t;
+ /*
+@@ -1412,6 +1416,37 @@
+         APR_BRIGADE_INSERT_TAIL(bb, bucket);
+     }
++#ifdef HAVE_TLS_ALPN
++    /* By this point, Application-Layer Protocol Negotiation (ALPN) should be 
++     * completed (if our version of OpenSSL supports it). If we haven't already, 
++     * find out which protocol was decided upon and inform other modules 
++     * by calling alpn_proto_negotiated_hook. 
++     */
++    if (!inctx->alpn_finished) {
++        SSLConnRec *sslconn = myConnConfig(f->c);
++        const unsigned char *next_proto = NULL;
++        unsigned next_proto_len = 0;
++        int n;
++        
++        if (sslconn->alpn_negofns) {
++            SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
++            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
++                          APLOGNO(02836) "SSL negotiated protocol: '%s'",
++                          (next_proto && next_proto_len)?
++                         apr_pstrmemdup(f->c->pool, (const char *)next_proto,
++                              next_proto_len) : "(null)");
++            for (n = 0; n < sslconn->alpn_negofns->nelts; n++) {
++                ssl_alpn_proto_negotiated fn =
++                APR_ARRAY_IDX(sslconn->alpn_negofns, n, ssl_alpn_proto_negotiated);
++                
++                if (fn(f->c, (const char *)next_proto, next_proto_len) == DONE)
++                break;
++            }
++        }
++        inctx->alpn_finished = 1;
++    }
++#endif
++
+     return APR_SUCCESS;
+ }
+@@ -1893,6 +1928,9 @@
+     inctx->block = APR_BLOCK_READ;
+     inctx->pool = c->pool;
+     inctx->filter_ctx = filter_ctx;
++#ifdef HAVE_TLS_ALPN
++    inctx->alpn_finished = 0;
++#endif
+ }
+ /* The request_rec pointer is passed in here only to ensure that the
+diff -ru modules/ssl/ssl_engine_kernel.c modules/ssl/ssl_engine_kernel.c
+--- modules/ssl/ssl_engine_kernel.c    2015-06-17 15:09:03.000000000 +0200
++++ modules/ssl/ssl_engine_kernel.c    2015-06-17 15:53:25.000000000 +0200
+@@ -29,6 +29,7 @@
+                                   time I was too famous.''
+                                             -- Unknown                */
+ #include "ssl_private.h"
++#include "mod_ssl.h"
+ #include "util_md5.h"
+ static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
+@@ -1889,55 +1890,69 @@
+ }
+ #ifdef HAVE_TLSEXT
++apr_status_t ssl_select_vhost(conn_rec *c)
++{
++    SSLConnRec *sslconn = myConnConfig(c);
++    const char *servername;
++    
++    if (sslconn == NULL || sslconn->ssl == NULL) {
++        return APR_EINVAL;
++    }
++    else if (sslconn->vhost_selected) {
++        return APR_SUCCESS;
++    }
++    
++    servername = SSL_get_servername(sslconn->ssl, TLSEXT_NAMETYPE_host_name);
++    if (servername) {
++        if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
++                                        (void *)servername)) {
++            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
++                          "SSL virtual host for servername %s found",
++                          servername);
++            sslconn->vhost_selected = 1;
++            return APR_SUCCESS;
++        }
++        else {
++            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
++                          "No matching SSL virtual host for servername "
++                          "%s found (using default/first virtual host)",
++                          servername);
++            /*
++             * RFC 6066 section 3 says "It is NOT RECOMMENDED to send
++             * a warning-level unrecognized_name(112) alert, because
++             * the client's behavior in response to warning-level alerts
++             * is unpredictable."
++             *
++             * To maintain backwards compatibility in mod_ssl, we
++             * no longer send any alert (neither warning- nor fatal-level),
++             * i.e. we take the second action suggested in RFC 6066:
++             * "If the server understood the ClientHello extension but
++             * does not recognize the server name, the server SHOULD take
++             * one of two actions: either abort the handshake by sending
++             * a fatal-level unrecognized_name(112) alert or continue
++             * the handshake."
++             */
++        }
++    }
++    else {
++        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02645)
++                      "Server name not provided via TLS extension "
++                      "(using default/first virtual host)");
++    }
++    
++    return APR_NOTFOUND;
++}
+ /*
+  * This callback function is executed when OpenSSL encounters an extended
+  * client hello with a server name indication extension ("SNI", cf. RFC 6066).
+  */
+ int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
+ {
+-    const char *servername =
+-                SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+     conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+-
+-    if (c) {
+-        if (servername) {
+-            if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
+-                                            (void *)servername)) {
+-                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
+-                              "SSL virtual host for servername %s found",
+-                              servername);
+-                return SSL_TLSEXT_ERR_OK;
+-            }
+-            else {
+-                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
+-                              "No matching SSL virtual host for servername "
+-                              "%s found (using default/first virtual host)",
+-                              servername);
+-                /*
+-                 * RFC 6066 section 3 says "It is NOT RECOMMENDED to send
+-                 * a warning-level unrecognized_name(112) alert, because
+-                 * the client's behavior in response to warning-level alerts
+-                 * is unpredictable."
+-                 *
+-                 * To maintain backwards compatibility in mod_ssl, we
+-                 * no longer send any alert (neither warning- nor fatal-level),
+-                 * i.e. we take the second action suggested in RFC 6066:
+-                 * "If the server understood the ClientHello extension but
+-                 * does not recognize the server name, the server SHOULD take
+-                 * one of two actions: either abort the handshake by sending
+-                 * a fatal-level unrecognized_name(112) alert or continue
+-                 * the handshake."
+-                 */
+-            }
+-        }
+-        else {
+-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02645)
+-                          "Server name not provided via TLS extension "
+-                          "(using default/first virtual host)");
+-        }
+-    }
+-
+-    return SSL_TLSEXT_ERR_NOACK;
++    apr_status_t status;
++    
++    status = ssl_select_vhost(c);
++    return (status == APR_SUCCESS)? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
+ }
+ /*
+@@ -2136,6 +2151,159 @@
+ }
+ #endif /* HAVE_TLS_SESSION_TICKETS */
++#ifdef HAVE_TLS_ALPN
++static int ssl_array_index(apr_array_header_t *array,
++                           const char *s)
++{
++    int i;
++    for (i = 0; i < array->nelts; i++) {
++        const char *p = APR_ARRAY_IDX(array, i, const char*);
++        if (!strcmp(p, s)) {
++            return i;
++        }
++    }
++    return -1;
++}
++
++/*
++ * Compare two ALPN protocol proposal. Result is similar to strcmp():
++ * 0 gives same precedence, >0 means proto1 is prefered.
++ */
++static int ssl_cmp_alpn_protos(modssl_ctx_t *ctx,
++                               const char *proto1,
++                               const char *proto2)
++{
++    /* TODO: we should have a mod_ssl configuration parameter. */
++    if (ctx && ctx->ssl_alpn_pref) {
++        int index1 = ssl_array_index(ctx->ssl_alpn_pref, proto1);
++        int index2 = ssl_array_index(ctx->ssl_alpn_pref, proto2);
++        if (index2 > index1) {
++            return (index1 >= 0)? 1 : -1;
++        }
++        else if (index1 > index2) {
++            return (index2 >= 0)? -1 : 1;
++        }
++    }
++    /* both have the same index (mabye -1 or no pref configured) and we compare
++     * the names so that spdy3 gets precedence over spdy2. That makes
++     * the outcome at least deterministic. */
++    return strcmp((const char *)proto1, (const char *)proto2);
++}
++
++/*
++ * This callback function is executed when the TLS Application Layer
++ * Protocol Negotiate Extension (ALPN, RFC 7301) is triggered by the client 
++ * hello, giving a list of desired protocol names (in descending preference) 
++ * to the server.
++ * The callback has to select a protocol name or return an error if none of
++ * the clients preferences is supported. 
++ * The selected protocol does not have to be on the client list, according
++ * to RFC 7301, so no checks are performed.
++ * The client protocol list is serialized as length byte followed by ascii
++ * characters (not null-terminated), followed by the next protocol name.
++ */
++int ssl_callback_alpn_select(SSL *ssl,
++                             const unsigned char **out, unsigned char *outlen,
++                             const unsigned char *in, unsigned int inlen, void *arg)
++{
++    conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
++    SSLConnRec *sslconn = myConnConfig(c);
++    server_rec *s       = mySrvFromConn(c);
++    SSLSrvConfigRec *sc = mySrvConfig(s);
++    modssl_ctx_t *mctx  = myCtxConfig(sslconn, sc);
++    const char *alpn_http1 = "http/1.1";
++    apr_array_header_t *client_protos;
++    apr_array_header_t *proposed_protos;
++    int i;
++    size_t len;
++
++    /* If the connection object is not available,
++     * then there's nothing for us to do. */
++    if (c == NULL) {
++        return SSL_TLSEXT_ERR_OK;
++    }
++    
++    /* Make sure that we set the correct vhost based on SNI servername
++     * information. This is supposed to be done by the SNI callback, but
++     * currently the ssl libs call the ALPN callback *before* the SNI one.
++     */
++    ssl_select_vhost(c);
++    
++    if (inlen == 0) {
++        // someone tries to trick us?
++        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02837)
++                      "ALPN client protocol list empty");
++        return SSL_TLSEXT_ERR_ALERT_FATAL;
++    }
++    
++    client_protos = apr_array_make(c->pool, 0, sizeof(char *));
++    for (i = 0; i < inlen; /**/) {
++        unsigned int plen = in[i++];
++        if (plen + i > inlen) {
++            // someone tries to trick us?
++            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02838)
++                          "ALPN protocol identier too long");
++            return SSL_TLSEXT_ERR_ALERT_FATAL;
++        }
++        APR_ARRAY_PUSH(client_protos, char*) =
++            apr_pstrndup(c->pool, (const char *)in+i, plen);
++        i += plen;
++    }
++    
++    proposed_protos = apr_array_make(c->pool, client_protos->nelts+1,
++                                     sizeof(char *));
++    
++    if (sslconn->alpn_proposefns != NULL) {
++        /* Invoke our alpn_propos_proto hooks, giving other modules a chance to
++         * propose protocol names for selection. We might have several such
++         * hooks installed and if two make a proposal, we need to give 
++         * preference to one.
++         */
++        for (i = 0; i < sslconn->alpn_proposefns->nelts; i++) {
++            ssl_alpn_propose_protos fn =
++                APR_ARRAY_IDX(sslconn->alpn_proposefns, i,
++                              ssl_alpn_propose_protos);
++            
++            if (fn(c, client_protos, proposed_protos) == DONE)
++                break;
++        }
++    }
++
++    if (proposed_protos->nelts <= 0) {
++        /* Regardless of installed hooks, the http/1.1 protocol is always
++         * supported by us. Choose it if none other matches. */
++        if (ssl_array_index(client_protos, alpn_http1) < 0) {
++            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02839)
++                          "none of the client ALPN protocols are supported");
++            return SSL_TLSEXT_ERR_ALERT_FATAL;
++        }
++        *out = (const unsigned char*)alpn_http1;
++        *outlen = (unsigned char)strlen(alpn_http1);
++        return SSL_TLSEXT_ERR_OK;
++    }
++    
++    /* Now select the most preferred protocol from the proposals. */
++    *out = APR_ARRAY_IDX(proposed_protos, 0, const unsigned char *);
++    for (i = 1; i < proposed_protos->nelts; ++i) {
++        const char *proto = APR_ARRAY_IDX(proposed_protos, i, const char*);
++        /* Do we prefer it over existing candidate? */
++        if (ssl_cmp_alpn_protos(mctx, (const char *)*out, proto) < 0) {
++            *out = (const unsigned char*)proto;
++        }
++    }
++    
++    len = strlen((const char*)*out);
++    if (len > 255) {
++        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840)
++                      "ALPN negotiated protocol name too long");
++        return SSL_TLSEXT_ERR_ALERT_FATAL;
++    }
++    *outlen = (unsigned char)len;
++
++    return SSL_TLSEXT_ERR_OK;
++}
++#endif
++
+ #ifdef HAVE_SRP
+ int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg)
+diff -ru modules/ssl/ssl_private.h modules/ssl/ssl_private.h
+--- modules/ssl/ssl_private.h  2015-06-17 15:09:03.000000000 +0200
++++ modules/ssl/ssl_private.h  2015-06-17 15:52:15.000000000 +0200
+@@ -182,6 +182,11 @@
+ #include <openssl/srp.h>
+ #endif
++/* ALPN Protocol Negotiation */
++#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
++#define HAVE_TLS_ALPN
++#endif
++
+ #endif /* !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name) */
+ /* mod_ssl headers */
+@@ -443,6 +448,12 @@
+                      * connection */
+     } reneg_state;
++#ifdef HAVE_TLS_ALPN
++    /* Poor man's inter-module optional hooks for ALPN. */
++    apr_array_header_t *alpn_proposefns; /* list of ssl_alpn_propose_protos callbacks */
++    apr_array_header_t *alpn_negofns; /* list of ssl_alpn_proto_negotiated callbacks. */
++#endif
++    int vhost_selected;
+     server_rec *server;
+ } SSLConnRec;
+@@ -622,6 +633,10 @@
+     SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
+     apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
+ #endif
++  
++#ifdef HAVE_TLS_ALPN
++  apr_array_header_t *ssl_alpn_pref; /* protocol names in order of preference */
++#endif
+ } modssl_ctx_t;
+ struct SSLSrvConfigRec {
+@@ -748,6 +763,10 @@
+ const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
+ #endif
++#ifdef HAVE_TLS_ALPN
++const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg, const char *protocol);
++#endif
++
+ #ifdef HAVE_SRP
+ const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char *arg);
+ const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const char *arg);
+@@ -789,6 +808,7 @@
+ void         ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
+ void         ssl_callback_Info(const SSL *, int, int);
+ #ifdef HAVE_TLSEXT
++apr_status_t ssl_select_vhost(conn_rec *c);
+ int          ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
+ #endif
+ #ifdef HAVE_TLS_SESSION_TICKETS
+@@ -796,6 +816,12 @@
+                                        EVP_CIPHER_CTX *, HMAC_CTX *, int);
+ #endif
++#ifdef HAVE_TLS_ALPN
++int ssl_callback_alpn_select(SSL *ssl, const unsigned char **out,
++                             unsigned char *outlen, const unsigned char *in,
++                             unsigned int inlen, void *arg);
++#endif
++
+ /**  Session Cache Support  */
+ apr_status_t ssl_scache_init(server_rec *, apr_pool_t *);
+ void         ssl_scache_status_register(apr_pool_t *p);
diff --git a/modules/http2/sandbox/httpd/patches/httpd-2.4.x-alpn-v4.patch b/modules/http2/sandbox/httpd/patches/httpd-2.4.x-alpn-v4.patch
new file mode 100644 (file)
index 0000000..733f4fa
--- /dev/null
@@ -0,0 +1,535 @@
+Index: CHANGES
+===================================================================
+--- CHANGES    (revision 1681752)
++++ CHANGES    (working copy)
+@@ -12,6 +12,12 @@ Changes with Apache 2.4.13
+      calls r:wsupgrade() can cause a child process crash. 
+      [Edward Lu <Chaosed0 gmail.com>]
++  *) mod_ssl: add ALPN support by allowing other modules to register callbacks
++     for negotiation of the application layer protocol.  PR 52210.
++     [Matthew Steele <mdsteele google com>, Joe Orton, Jim Jagielski,
++      Stefan Eissing <stefan eissing org>, Rainer Jung, Ruediger Pluem,
++      Kaspar Brand]
++
+   *) mod_ssl OCSP Stapling: Don't block initial handshakes while refreshing
+      the OCSP response for a different certificate.  mod_ssl has an additional
+      global mutex, "ssl-stapling-refresh".  PR 57131 (partial fix).
+Index: docs/manual/mod/mod_ssl.xml
+===================================================================
+--- docs/manual/mod/mod_ssl.xml        (revision 1681752)
++++ docs/manual/mod/mod_ssl.xml        (working copy)
+@@ -2659,4 +2659,32 @@ SSLOpenSSLConfCmd SignatureAlgorithms RSA+SHA384:E
+ </usage>
+ </directivesynopsis>
++<directivesynopsis>
++<name>SSLALPNPreference</name>
++<description>Configure protocol preference for Application-Layer Protocol Negotiation (RFC 7301)</description>
++<syntax>SSLALPNPreference <em>protocol-id</em> ...</syntax>
++<contextlist><context>server config</context>
++<context>virtual host</context></contextlist>
++<compatibility>Available if using OpenSSL 1.0.2 or later</compatibility>
++
++<usage>
++<p>This directive overrides preferences by implementations of application-layer
++protocols during negotiation. Protocol IDs must be specified in terms of the
++"Identification Sequence" from IANA's Application-Layer Protocol
++Negotiation (ALPN) Protocol IDs registry. The protocol specified first gets
++precedence over later or unspecified protocols. This list is evaluated
++against client capabilities.</p>
++
++<example><title>Examples</title>
++<highlight language="config">
++SSLALPNPreference h2 spdy/3
++</highlight>
++<p>For clients which support both protocols, <code>h2</code> will be selected. For
++clients which only support <code>spdy/3</code>, that one will be chosen. If a client
++supports only protocol X and there is support for X in httpd, the
++negotiation will select X.</p>
++</example>
++</usage>
++</directivesynopsis>
++
+ </modulesynopsis>
+Index: modules/ssl/mod_ssl.c
+===================================================================
+--- modules/ssl/mod_ssl.c      (revision 1681752)
++++ modules/ssl/mod_ssl.c      (working copy)
+@@ -273,6 +273,12 @@ static const command_rec ssl_config_cmds[] = {
+                 "OpenSSL configuration command")
+ #endif
++#ifdef HAVE_TLS_ALPN
++    SSL_CMD_SRV(ALPNPreference, ITERATE,
++                "Preference in Application-Layer Protocol Negotiation (ALPN), "
++                "protocols are chosen in the specified order")
++#endif
++
+     /* Deprecated directives. */
+     AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL,
+       "SSLLog directive is no longer supported - use ErrorLog."),
+@@ -426,6 +432,37 @@ int ssl_engine_disable(conn_rec *c)
+     return 1;
+ }
++static int modssl_register_alpn(conn_rec *c,
++                               ssl_alpn_propose_protos advertisefn,
++                               ssl_alpn_proto_negotiated negotiatedfn)
++{
++#ifdef HAVE_TLS_ALPN
++    SSLConnRec *sslconn = myConnConfig(c);
++
++    if (!sslconn) {
++        return DECLINED;
++    }
++
++    if (!sslconn->alpn_proposefns) {
++        sslconn->alpn_proposefns =
++            apr_array_make(c->pool, 5, sizeof(ssl_alpn_propose_protos));
++        sslconn->alpn_negofns =
++            apr_array_make(c->pool, 5, sizeof(ssl_alpn_proto_negotiated));
++    }
++
++    if (advertisefn)
++        APR_ARRAY_PUSH(sslconn->alpn_proposefns, ssl_alpn_propose_protos) =
++            advertisefn;
++    if (negotiatedfn)
++        APR_ARRAY_PUSH(sslconn->alpn_negofns, ssl_alpn_proto_negotiated) =
++            negotiatedfn;
++
++    return OK;
++#else
++    return DECLINED;
++#endif
++}
++
+ int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
+ {
+     SSLSrvConfigRec *sc;
+@@ -588,6 +625,7 @@ static void ssl_register_hooks(apr_pool_t *p)
+     APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
+     APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
++    APR_REGISTER_OPTIONAL_FN(modssl_register_alpn);
+     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
+                               AUTHZ_PROVIDER_VERSION,
+Index: modules/ssl/mod_ssl.h
+===================================================================
+--- modules/ssl/mod_ssl.h      (revision 1681752)
++++ modules/ssl/mod_ssl.h      (working copy)
+@@ -63,5 +63,46 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (co
+ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
++/** The alpn_propose_proto callback allows other modules to propose
++ * the name of the protocol that will be chosen during the
++ * Application-Layer Protocol Negotiation (ALPN) portion of the SSL handshake.
++ * The callback is given the connection and a list of NULL-terminated
++ * protocol strings as supported by the client.  If this client_protos is 
++ * non-empty, it must pick its preferred protocol from that list. Otherwise
++ * it should add its supported protocols in order of precedence.
++ * The callback should not yet modify the connection or install any filters
++ * as its proposal(s) may be overridden by another callback or server 
++ * configuration. 
++ * It should return OK or, to prevent further processing of (other modules') 
++ * callbacks, return DONE.
++ */
++typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
++                                       apr_array_header_t *client_protos,
++                                       apr_array_header_t *proposed_protos);
++
++/** The alpn_proto_negotiated callback allows other modules to discover
++ * the name of the protocol that was chosen during the Application-Layer
++ * Protocol Negotiation (ALPN) portion of the SSL handshake.  
++ * The callback is given the connection, a
++ * non-NUL-terminated string containing the protocol name, and the
++ * length of the string; it should do something appropriate
++ * (i.e. insert or remove filters) and return OK. To prevent further
++ * processing of (other modules') callbacks, return DONE. */
++typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
++                                         const char *proto_name,
++                                         apr_size_t proto_name_len);
++
++/* An optional function which can be used to register a pair of callbacks 
++ * for ALPN handling.
++ * This optional function should be invoked from a pre_connection hook 
++ * which runs *after* mod_ssl.c's pre_connection hook.  The function returns 
++ * OK if the callbacks are registered, or DECLINED otherwise (for example if 
++ * mod_ssl does not support ALPN).
++ */
++APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
++                        (conn_rec *conn,
++                         ssl_alpn_propose_protos proposefn,
++                         ssl_alpn_proto_negotiated negotiatedfn));
++
+ #endif /* __MOD_SSL_H__ */
+ /** @} */
+Index: modules/ssl/ssl_engine_config.c
+===================================================================
+--- modules/ssl/ssl_engine_config.c    (revision 1681752)
++++ modules/ssl/ssl_engine_config.c    (working copy)
+@@ -161,6 +161,9 @@ static void modssl_ctx_init(modssl_ctx_t *mctx, ap
+     SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
+     mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
+ #endif
++#ifdef HAVE_TLS_ALPN
++    mctx->ssl_alpn_pref = apr_array_make(p, 5, sizeof(const char *));
++#endif
+ }
+ static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc,
+@@ -305,6 +308,9 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
+ #ifdef HAVE_SSL_CONF_CMD
+     cfgMergeArray(ssl_ctx_param);
+ #endif
++#ifdef HAVE_TLS_ALPN
++    cfgMergeArray(ssl_alpn_pref);
++#endif
+ }
+ static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p,
+@@ -1890,6 +1896,16 @@ const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *c
+ }
+ #endif
++#ifdef HAVE_TLS_ALPN
++const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg,
++                                      const char *protocol)
++{
++    SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
++    APR_ARRAY_PUSH(sc->server->ssl_alpn_pref, const char *) = protocol;
++    return NULL;
++}
++#endif
++
+ #ifdef HAVE_SRP
+ const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg,
+Index: modules/ssl/ssl_engine_init.c
+===================================================================
+--- modules/ssl/ssl_engine_init.c      (revision 1681752)
++++ modules/ssl/ssl_engine_init.c      (working copy)
+@@ -625,6 +625,10 @@ static void ssl_init_ctx_callbacks(server_rec *s,
+     SSL_CTX_set_tmp_dh_callback(ctx,  ssl_callback_TmpDH);
+     SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
++
++#ifdef HAVE_TLS_ALPN
++    SSL_CTX_set_alpn_select_cb(ctx, ssl_callback_alpn_select, NULL);
++#endif
+ }
+ static apr_status_t ssl_init_ctx_verify(server_rec *s,
+Index: modules/ssl/ssl_engine_io.c
+===================================================================
+--- modules/ssl/ssl_engine_io.c        (revision 1681752)
++++ modules/ssl/ssl_engine_io.c        (working copy)
+@@ -28,6 +28,7 @@
+                                   core keeps dumping.''
+                                             -- Unknown    */
+ #include "ssl_private.h"
++#include "mod_ssl.h"
+ #include "apr_date.h"
+ /*  _________________________________________________________________
+@@ -297,6 +298,9 @@ typedef struct {
+     apr_pool_t *pool;
+     char buffer[AP_IOBUFSIZE];
+     ssl_filter_ctx_t *filter_ctx;
++#ifdef HAVE_TLS_ALPN
++    int alpn_finished;  /* 1 if ALPN has finished, 0 otherwise */
++#endif
+ } bio_filter_in_ctx_t;
+ /*
+@@ -1412,6 +1416,40 @@ static apr_status_t ssl_io_filter_input(ap_filter_
+         APR_BRIGADE_INSERT_TAIL(bb, bucket);
+     }
++#ifdef HAVE_TLS_ALPN
++    /* By this point, Application-Layer Protocol Negotiation (ALPN) should be 
++     * completed (if our version of OpenSSL supports it). If we haven't already, 
++     * find out which protocol was decided upon and inform other modules 
++     * by calling alpn_proto_negotiated_hook. 
++     */
++    if (!inctx->alpn_finished) {
++        SSLConnRec *sslconn = myConnConfig(f->c);
++        const unsigned char *next_proto = NULL;
++        unsigned next_proto_len = 0;
++        int n;
++
++        if (sslconn->alpn_negofns) {
++            SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len);
++            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
++                          APLOGNO(02836) "ALPN selected protocol: '%s'",
++                          (next_proto && next_proto_len) ?
++                              apr_pstrmemdup(f->c->pool,
++                                             (const char *)next_proto,
++                                             next_proto_len) :
++                              "(null)");
++            for (n = 0; n < sslconn->alpn_negofns->nelts; n++) {
++                ssl_alpn_proto_negotiated fn =
++                    APR_ARRAY_IDX(sslconn->alpn_negofns, n,
++                                  ssl_alpn_proto_negotiated);
++
++                if (fn(f->c, (const char *)next_proto, next_proto_len) == DONE)
++                    break;
++            }
++        }
++        inctx->alpn_finished = 1;
++    }
++#endif
++
+     return APR_SUCCESS;
+ }
+@@ -1893,6 +1931,9 @@ static void ssl_io_input_add_filter(ssl_filter_ctx
+     inctx->block = APR_BLOCK_READ;
+     inctx->pool = c->pool;
+     inctx->filter_ctx = filter_ctx;
++#ifdef HAVE_TLS_ALPN
++    inctx->alpn_finished = 0;
++#endif
+ }
+ /* The request_rec pointer is passed in here only to ensure that the
+Index: modules/ssl/ssl_engine_kernel.c
+===================================================================
+--- modules/ssl/ssl_engine_kernel.c    (revision 1681752)
++++ modules/ssl/ssl_engine_kernel.c    (working copy)
+@@ -29,6 +29,7 @@
+                                   time I was too famous.''
+                                             -- Unknown                */
+ #include "ssl_private.h"
++#include "mod_ssl.h"
+ #include "util_md5.h"
+ static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
+@@ -2149,6 +2150,152 @@ int ssl_callback_SessionTicket(SSL *ssl,
+ }
+ #endif /* HAVE_TLS_SESSION_TICKETS */
++#ifdef HAVE_TLS_ALPN
++static int ssl_array_index(apr_array_header_t *array, const char *s)
++{
++    int i;
++    for (i = 0; i < array->nelts; i++) {
++        const char *p = APR_ARRAY_IDX(array, i, const char *);
++        if (!strcmp(p, s)) {
++            return i;
++        }
++    }
++    return -1;
++}
++
++/*
++ * Compare two ALPN protocol proposal. Result is similar to strcmp():
++ * 0 gives same precedence, >0 means proto1 is preferred.
++ */
++static int ssl_cmp_alpn_protos(modssl_ctx_t *ctx,
++                               const char *proto1,
++                               const char *proto2)
++{
++    if (ctx && ctx->ssl_alpn_pref) {
++        int index1 = ssl_array_index(ctx->ssl_alpn_pref, proto1);
++        int index2 = ssl_array_index(ctx->ssl_alpn_pref, proto2);
++        if (index2 > index1) {
++            return (index1 >= 0) ? 1 : -1;
++        }
++        else if (index1 > index2) {
++            return (index2 >= 0) ? -1 : 1;
++        }
++    }
++    /* both have the same index (mabye -1 or no pref configured) and we compare
++     * the names so that spdy3 gets precedence over spdy2. That makes
++     * the outcome at least deterministic. */
++    return strcmp((const char *)proto1, (const char *)proto2);
++}
++
++/*
++ * This callback function is executed when the TLS Application-Layer
++ * Protocol Negotiation Extension (ALPN, RFC 7301) is triggered by the Client
++ * Hello, giving a list of desired protocol names (in descending preference) 
++ * to the server.
++ * The callback has to select a protocol name or return an error if none of
++ * the clients preferences is supported.
++ * The selected protocol does not have to be on the client list, according
++ * to RFC 7301, so no checks are performed.
++ * The client protocol list is serialized as length byte followed by ASCII
++ * characters (not null-terminated), followed by the next protocol name.
++ */
++int ssl_callback_alpn_select(SSL *ssl,
++                             const unsigned char **out, unsigned char *outlen,
++                             const unsigned char *in, unsigned int inlen,
++                             void *arg)
++{
++    conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
++    SSLConnRec *sslconn = myConnConfig(c);
++    server_rec *s = mySrvFromConn(c);
++    SSLSrvConfigRec *sc = mySrvConfig(s);
++    modssl_ctx_t *mctx = myCtxConfig(sslconn, sc);
++    const char *alpn_http1 = "http/1.1";
++    apr_array_header_t *client_protos;
++    apr_array_header_t *proposed_protos;
++    int i;
++    size_t len;
++
++    /* If the connection object is not available,
++     * then there's nothing for us to do. */
++    if (c == NULL) {
++        return SSL_TLSEXT_ERR_OK;
++    }
++
++    if (inlen == 0) {
++        // someone tries to trick us?
++        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02837)
++                      "ALPN client protocol list empty");
++        return SSL_TLSEXT_ERR_ALERT_FATAL;
++    }
++
++    client_protos = apr_array_make(c->pool, 0, sizeof(char *));
++    for (i = 0; i < inlen; /**/) {
++        unsigned int plen = in[i++];
++        if (plen + i > inlen) {
++            // someone tries to trick us?
++            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02838)
++                          "ALPN protocol identifier too long");
++            return SSL_TLSEXT_ERR_ALERT_FATAL;
++        }
++        APR_ARRAY_PUSH(client_protos, char *) =
++            apr_pstrndup(c->pool, (const char *)in+i, plen);
++        i += plen;
++    }
++
++    proposed_protos = apr_array_make(c->pool, client_protos->nelts+1,
++                                     sizeof(char *));
++
++    if (sslconn->alpn_proposefns != NULL) {
++        /* Invoke our alpn_propose functions, giving other modules a chance to
++         * propose protocol names for selection. We might have several such
++         * functions installed and if two make a proposal, we need to give
++         * preference to one.
++         */
++        for (i = 0; i < sslconn->alpn_proposefns->nelts; i++) {
++            ssl_alpn_propose_protos fn =
++                APR_ARRAY_IDX(sslconn->alpn_proposefns, i,
++                              ssl_alpn_propose_protos);
++
++            if (fn(c, client_protos, proposed_protos) == DONE)
++                break;
++        }
++    }
++
++    if (proposed_protos->nelts <= 0) {
++        /* Regardless of installed hooks, the http/1.1 protocol is always
++         * supported by us. Choose it if none other matches. */
++        if (ssl_array_index(client_protos, alpn_http1) < 0) {
++            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02839)
++                          "none of the client ALPN protocols are supported");
++            return SSL_TLSEXT_ERR_ALERT_FATAL;
++        }
++        *out = (const unsigned char*)alpn_http1;
++        *outlen = (unsigned char)strlen(alpn_http1);
++        return SSL_TLSEXT_ERR_OK;
++    }
++
++    /* Now select the most preferred protocol from the proposals. */
++    *out = APR_ARRAY_IDX(proposed_protos, 0, const unsigned char *);
++    for (i = 1; i < proposed_protos->nelts; ++i) {
++        const char *proto = APR_ARRAY_IDX(proposed_protos, i, const char *);
++        /* Do we prefer it over existing candidate? */
++        if (ssl_cmp_alpn_protos(mctx, (const char *)*out, proto) < 0) {
++            *out = (const unsigned char *)proto;
++        }
++    }
++
++    len = strlen((const char*)*out);
++    if (len > 255) {
++        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840)
++                      "ALPN negotiated protocol name too long");
++        return SSL_TLSEXT_ERR_ALERT_FATAL;
++    }
++    *outlen = (unsigned char)len;
++
++    return SSL_TLSEXT_ERR_OK;
++}
++#endif /* HAVE_TLS_ALPN */
++
+ #ifdef HAVE_SRP
+ int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg)
+Index: modules/ssl/ssl_private.h
+===================================================================
+--- modules/ssl/ssl_private.h  (revision 1681752)
++++ modules/ssl/ssl_private.h  (working copy)
+@@ -182,6 +182,11 @@
+ #include <openssl/srp.h>
+ #endif
++/* ALPN Protocol Negotiation */
++#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
++#define HAVE_TLS_ALPN
++#endif
++
+ #endif /* !defined(OPENSSL_NO_TLSEXT) && defined(SSL_set_tlsext_host_name) */
+ /* mod_ssl headers */
+@@ -443,6 +448,12 @@ typedef struct {
+                      * connection */
+     } reneg_state;
++#ifdef HAVE_TLS_ALPN
++    /* Poor man's inter-module optional hooks for ALPN. */
++    apr_array_header_t *alpn_proposefns; /* list of ALPN propose callbacks */
++    apr_array_header_t *alpn_negofns; /* list of ALPN negotiation callbacks. */
++#endif
++
+     server_rec *server;
+ } SSLConnRec;
+@@ -624,6 +635,10 @@ typedef struct {
+     SSL_CONF_CTX *ssl_ctx_config; /* Configuration context */
+     apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
+ #endif
++  
++#ifdef HAVE_TLS_ALPN
++  apr_array_header_t *ssl_alpn_pref; /* list of ALPN protocol IDs */
++#endif
+ } modssl_ctx_t;
+ struct SSLSrvConfigRec {
+@@ -750,6 +765,10 @@ const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd,
+ const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
+ #endif
++#ifdef HAVE_TLS_ALPN
++const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg, const char *protocol);
++#endif
++
+ #ifdef HAVE_SRP
+ const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, const char *arg);
+ const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, const char *arg);
+@@ -798,6 +817,12 @@ int         ssl_callback_SessionTicket(SSL *, unsi
+                                        EVP_CIPHER_CTX *, HMAC_CTX *, int);
+ #endif
++#ifdef HAVE_TLS_ALPN
++int ssl_callback_alpn_select(SSL *ssl, const unsigned char **out,
++                             unsigned char *outlen, const unsigned char *in,
++                             unsigned int inlen, void *arg);
++#endif
++
+ /**  Session Cache Support  */
+ apr_status_t ssl_scache_init(server_rec *, apr_pool_t *);
+ void         ssl_scache_status_register(apr_pool_t *p);
+Index: .
+===================================================================
+--- .  (revision 1681752)
++++ .  (working copy)
+
+Property changes on: .
+___________________________________________________________________
+Modified: svn:mergeinfo
+   Merged /httpd/httpd/trunk:r1332643,1487772,1670397,1670434,1670436,1670578,1670738,1675459,1676004,1676709,1681741,1681746
diff --git a/modules/http2/sandbox/httpd/patches/httpd-alpn-v4-v5.patch b/modules/http2/sandbox/httpd/patches/httpd-alpn-v4-v5.patch
new file mode 100644 (file)
index 0000000..07fadc7
--- /dev/null
@@ -0,0 +1,148 @@
+diff -ru modules/ssl/ssl_engine_kernel.c modules/ssl/ssl_engine_kernel.c
+--- modules/ssl/ssl_engine_kernel.c    2015-06-17 16:55:27.000000000 +0200
++++ modules/ssl/ssl_engine_kernel.c    2015-06-17 16:55:46.000000000 +0200
+@@ -1890,55 +1890,69 @@
+ }
+ #ifdef HAVE_TLSEXT
++apr_status_t ssl_select_vhost(conn_rec *c)
++{
++    SSLConnRec *sslconn = myConnConfig(c);
++    const char *servername;
++    
++    if (sslconn == NULL || sslconn->ssl == NULL) {
++        return APR_EINVAL;
++    }
++    else if (sslconn->vhost_selected) {
++        return APR_SUCCESS;
++    }
++    
++    servername = SSL_get_servername(sslconn->ssl, TLSEXT_NAMETYPE_host_name);
++    if (servername) {
++        if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
++                                        (void *)servername)) {
++            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
++                          "SSL virtual host for servername %s found",
++                          servername);
++            sslconn->vhost_selected = 1;
++            return APR_SUCCESS;
++        }
++        else {
++            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
++                          "No matching SSL virtual host for servername "
++                          "%s found (using default/first virtual host)",
++                          servername);
++            /*
++             * RFC 6066 section 3 says "It is NOT RECOMMENDED to send
++             * a warning-level unrecognized_name(112) alert, because
++             * the client's behavior in response to warning-level alerts
++             * is unpredictable."
++             *
++             * To maintain backwards compatibility in mod_ssl, we
++             * no longer send any alert (neither warning- nor fatal-level),
++             * i.e. we take the second action suggested in RFC 6066:
++             * "If the server understood the ClientHello extension but
++             * does not recognize the server name, the server SHOULD take
++             * one of two actions: either abort the handshake by sending
++             * a fatal-level unrecognized_name(112) alert or continue
++             * the handshake."
++             */
++        }
++    }
++    else {
++        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02645)
++                      "Server name not provided via TLS extension "
++                      "(using default/first virtual host)");
++    }
++    
++    return APR_NOTFOUND;
++}
+ /*
+  * This callback function is executed when OpenSSL encounters an extended
+  * client hello with a server name indication extension ("SNI", cf. RFC 6066).
+  */
+ int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
+ {
+-    const char *servername =
+-                SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+     conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
+-
+-    if (c) {
+-        if (servername) {
+-            if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
+-                                            (void *)servername)) {
+-                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
+-                              "SSL virtual host for servername %s found",
+-                              servername);
+-                return SSL_TLSEXT_ERR_OK;
+-            }
+-            else {
+-                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
+-                              "No matching SSL virtual host for servername "
+-                              "%s found (using default/first virtual host)",
+-                              servername);
+-                /*
+-                 * RFC 6066 section 3 says "It is NOT RECOMMENDED to send
+-                 * a warning-level unrecognized_name(112) alert, because
+-                 * the client's behavior in response to warning-level alerts
+-                 * is unpredictable."
+-                 *
+-                 * To maintain backwards compatibility in mod_ssl, we
+-                 * no longer send any alert (neither warning- nor fatal-level),
+-                 * i.e. we take the second action suggested in RFC 6066:
+-                 * "If the server understood the ClientHello extension but
+-                 * does not recognize the server name, the server SHOULD take
+-                 * one of two actions: either abort the handshake by sending
+-                 * a fatal-level unrecognized_name(112) alert or continue
+-                 * the handshake."
+-                 */
+-            }
+-        }
+-        else {
+-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02645)
+-                          "Server name not provided via TLS extension "
+-                          "(using default/first virtual host)");
+-        }
+-    }
+-
+-    return SSL_TLSEXT_ERR_NOACK;
++    apr_status_t status;
++    
++    status = ssl_select_vhost(c);
++    return (status == APR_SUCCESS)? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
+ }
+ /*
+@@ -2209,6 +2223,12 @@
+         return SSL_TLSEXT_ERR_OK;
+     }
+     
++    /* Make sure that we set the correct vhost based on SNI servername
++     * information. This is supposed to be done by the SNI callback, but
++     * currently the ssl libs call the ALPN callback *before* the SNI one.
++     */
++    ssl_select_vhost(c);
++    
+     if (inlen == 0) {
+         // someone tries to trick us?
+         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02837)
+diff -ru modules/ssl/ssl_private.h modules/ssl/ssl_private.h
+--- modules/ssl/ssl_private.h  2015-06-17 16:55:27.000000000 +0200
++++ modules/ssl/ssl_private.h  2015-06-17 16:55:46.000000000 +0200
+@@ -453,7 +453,7 @@
+     apr_array_header_t *alpn_proposefns; /* list of ssl_alpn_propose_protos callbacks */
+     apr_array_header_t *alpn_negofns; /* list of ssl_alpn_proto_negotiated callbacks. */
+ #endif
+-
++    int vhost_selected;
+     server_rec *server;
+ } SSLConnRec;
+@@ -808,6 +808,7 @@
+ void         ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
+ void         ssl_callback_Info(const SSL *, int, int);
+ #ifdef HAVE_TLSEXT
++apr_status_t ssl_select_vhost(conn_rec *c);
+ int          ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
+ #endif
+ #ifdef HAVE_TLS_SESSION_TICKETS
diff --git a/modules/http2/sandbox/httpd/patches/httpd-npn.unified.diff.patch b/modules/http2/sandbox/httpd/patches/httpd-npn.unified.diff.patch
new file mode 100644 (file)
index 0000000..e7b0b5e
--- /dev/null
@@ -0,0 +1,366 @@
+diff -ru modules/ssl/mod_ssl.c modules/ssl/mod_ssl.c
+--- modules/ssl/mod_ssl.c      2015-04-27 17:41:19.000000000 +0200
++++ modules/ssl/mod_ssl.c      2015-04-27 17:45:56.000000000 +0200
+@@ -273,7 +273,7 @@
+               "OpenSSL configuration command")
+ #endif
+-#ifdef HAVE_TLS_ALPN
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+     SSL_CMD_SRV(ALPNPreference, ITERATE,
+                 "Preference in Application-Layer Protocol Negotiation (ALPN), "
+                 "protocols are chosen in the specified order")
+@@ -429,6 +429,37 @@
+     return 1;
+ }
++static int modssl_register_npn(conn_rec *c, 
++                               ssl_npn_advertise_protos advertisefn,
++                               ssl_npn_proto_negotiated negotiatedfn)
++{
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
++    SSLConnRec *sslconn = myConnConfig(c);
++
++    if (!sslconn) {
++        return DECLINED;
++    }
++
++    if (!sslconn->npn_advertfns) {
++        sslconn->npn_advertfns = 
++            apr_array_make(c->pool, 5, sizeof(ssl_npn_advertise_protos));
++        sslconn->npn_negofns = 
++            apr_array_make(c->pool, 5, sizeof(ssl_npn_proto_negotiated));
++    }
++
++    if (advertisefn)
++        APR_ARRAY_PUSH(sslconn->npn_advertfns, ssl_npn_advertise_protos) =
++            advertisefn;
++    if (negotiatedfn)
++        APR_ARRAY_PUSH(sslconn->npn_negofns, ssl_npn_proto_negotiated) =
++            negotiatedfn;
++
++    return OK;
++#else
++    return DECLINED;
++#endif
++}
++
+ static int modssl_register_alpn(conn_rec *c,
+                                ssl_alpn_propose_protos advertisefn,
+                                ssl_alpn_proto_negotiated negotiatedfn)
+@@ -623,6 +653,7 @@
+     APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
+     APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
++    APR_REGISTER_OPTIONAL_FN(modssl_register_npn);
+     APR_REGISTER_OPTIONAL_FN(modssl_register_alpn);
+     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ssl",
+diff -ru modules/ssl/mod_ssl.h modules/ssl/mod_ssl.h
+--- modules/ssl/mod_ssl.h      2015-04-27 17:41:19.000000000 +0200
++++ modules/ssl/mod_ssl.h      2015-04-27 17:45:56.000000000 +0200
+@@ -63,6 +63,41 @@
+ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
++/** The npn_advertise_protos callback allows another modules to add
++ * entries to the list of protocol names advertised by the server
++ * during the Next Protocol Negotiation (NPN) portion of the SSL
++ * handshake.  The callback is given the connection and an APR array;
++ * it should push one or more char*'s pointing to NUL-terminated
++ * strings (such as "http/1.1" or "spdy/2") onto the array and return
++ * OK.  To prevent further processing of (other modules') callbacks,
++ * return DONE. */
++typedef int (*ssl_npn_advertise_protos)(conn_rec *connection, 
++                                        apr_array_header_t *protos);
++
++/** The npn_proto_negotiated callback allows other modules to discover
++ * the name of the protocol that was chosen during the Next Protocol
++ * Negotiation (NPN) portion of the SSL handshake.  Note that this may
++ * be the empty string (in which case modules should probably assume
++ * HTTP), or it may be a protocol that was never even advertised by
++ * the server.  The callback is given the connection, a
++ * non-NUL-terminated string containing the protocol name, and the
++ * length of the string; it should do something appropriate
++ * (i.e. insert or remove filters) and return OK.  To prevent further
++ * processing of (other modules') callbacks, return DONE. */
++typedef int (*ssl_npn_proto_negotiated)(conn_rec *connection, 
++                                        const char *proto_name,
++                                        apr_size_t proto_name_len);
++
++/* An optional function which can be used to register a pair of
++ * callbacks for NPN handling.  This optional function should be
++ * invoked from a pre_connection hook which runs *after* mod_ssl.c's
++ * pre_connection hook.  The function returns OK if the callbacks are
++ * register, or DECLINED otherwise (for example if mod_ssl does not
++ * support NPN).  */
++APR_DECLARE_OPTIONAL_FN(int, modssl_register_npn, (conn_rec *conn, 
++                                                   ssl_npn_advertise_protos advertisefn,
++                                                   ssl_npn_proto_negotiated negotiatedfn));
++
+ /** The alpn_propose_proto callback allows other modules to propose
+  * the name of the protocol that will be chosen during the
+  * Application-Layer Protocol Negotiation (ALPN) portion of the SSL handshake.
+diff -ru modules/ssl/ssl_engine_config.c modules/ssl/ssl_engine_config.c
+--- modules/ssl/ssl_engine_config.c    2015-04-27 17:41:19.000000000 +0200
++++ modules/ssl/ssl_engine_config.c    2015-04-27 17:45:56.000000000 +0200
+@@ -159,7 +159,7 @@
+     SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE);
+     mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t));
+ #endif
+-#ifdef HAVE_TLS_ALPN
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+     mctx->ssl_alpn_pref = apr_array_make(p, 5, sizeof(const char *));
+ #endif
+ }
+@@ -301,7 +301,7 @@
+ #ifdef HAVE_SSL_CONF_CMD
+     cfgMergeArray(ssl_ctx_param);
+ #endif
+-#ifdef HAVE_TLS_ALPN
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+     cfgMergeArray(ssl_alpn_pref);
+ #endif
+ }
+@@ -1875,7 +1875,7 @@
+ }
+ #endif
+-#ifdef HAVE_TLS_ALPN
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+ const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg,
+                                       const char *protocol)
+ {
+diff -ru modules/ssl/ssl_engine_init.c modules/ssl/ssl_engine_init.c
+--- modules/ssl/ssl_engine_init.c      2015-04-27 17:41:19.000000000 +0200
++++ modules/ssl/ssl_engine_init.c      2015-04-27 17:45:56.000000000 +0200
+@@ -628,6 +629,11 @@
+     SSL_CTX_set_alpn_select_cb(
+        ctx, ssl_callback_alpn_select, NULL);
+ #endif
++
++#ifdef HAVE_TLS_NPN
++    SSL_CTX_set_next_protos_advertised_cb(
++        ctx, ssl_callback_AdvertiseNextProtos, NULL);
++#endif
+ }
+ static apr_status_t ssl_init_ctx_verify(server_rec *s,
+diff -ru modules/ssl/ssl_engine_io.c modules/ssl/ssl_engine_io.c
+--- modules/ssl/ssl_engine_io.c        2015-04-27 17:41:19.000000000 +0200
++++ modules/ssl/ssl_engine_io.c        2015-04-27 17:45:56.000000000 +0200
+@@ -298,7 +298,8 @@
+     apr_pool_t *pool;
+     char buffer[AP_IOBUFSIZE];
+     ssl_filter_ctx_t *filter_ctx;
+-#ifdef HAVE_TLS_ALPN
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
++    int npn_finished;  /* 1 if NPN has finished, 0 otherwise */
+     int alpn_finished;  /* 1 if ALPN has finished, 0 otherwise */
+ #endif
+ } bio_filter_in_ctx_t;
+@@ -1447,6 +1446,37 @@
+     }
+ #endif
++#ifdef HAVE_TLS_NPN
++    /* By this point, Next Protocol Negotiation (NPN) should be completed (if
++     * our version of OpenSSL supports it).  If we haven't already, find out
++     * which protocol was decided upon and inform other modules by calling
++     * npn_proto_negotiated_hook. */
++    if (!inctx->npn_finished) {
++        SSLConnRec *sslconn = myConnConfig(f->c);
++        const unsigned char *next_proto = NULL;
++        unsigned next_proto_len = 0;
++        int n;
++
++        if (sslconn->npn_negofns) {
++            SSL_get0_next_proto_negotiated(
++                inctx->ssl, &next_proto, &next_proto_len);
++            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
++                          APLOGNO(02306) "SSL NPN negotiated protocol: '%*s'",
++                          next_proto_len, (const char*)next_proto);
++            
++            for (n = 0; n < sslconn->npn_negofns->nelts; n++) {
++                ssl_npn_proto_negotiated fn = 
++                    APR_ARRAY_IDX(sslconn->npn_negofns, n, ssl_npn_proto_negotiated);
++                
++                if (fn(f->c, (const char *)next_proto, next_proto_len) == DONE)
++                    break;
++            }
++        }
++            
++        inctx->npn_finished = 1;
++    }
++#endif
++
+     return APR_SUCCESS;
+ }
+@@ -1928,9 +1958,8 @@
+     inctx->block = APR_BLOCK_READ;
+     inctx->pool = c->pool;
+     inctx->filter_ctx = filter_ctx;
+-#ifdef HAVE_TLS_ALPN
++    inctx->npn_finished = 0;
+     inctx->alpn_finished = 0;
+-#endif
+ }
+ /* The request_rec pointer is passed in here only to ensure that the
+diff -ru modules/ssl/ssl_engine_kernel.c modules/ssl/ssl_engine_kernel.c
+--- modules/ssl/ssl_engine_kernel.c    2015-04-27 17:41:19.000000000 +0200
++++ modules/ssl/ssl_engine_kernel.c    2015-04-27 17:45:56.000000000 +0200
+@@ -2283,6 +2282,96 @@
+     return SSL_TLSEXT_ERR_OK;
+ }
+ #endif
++#if defined(HAVE_TLS_NPN)
++/*
++ * This callback function is executed when SSL needs to decide what protocols
++ * to advertise during Next Protocol Negotiation (NPN).  It must produce a
++ * string in wire format -- a sequence of length-prefixed strings -- indicating
++ * the advertised protocols.  Refer to SSL_CTX_set_next_protos_advertised_cb
++ * in OpenSSL for reference.
++ */
++int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data_out,
++                                     unsigned int *size_out, void *arg)
++{
++    conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
++    SSLConnRec *sslconn = myConnConfig(c);
++    apr_array_header_t *protos;
++    int num_protos;
++    unsigned int size;
++    int i;
++    unsigned char *data;
++    unsigned char *start;
++
++    *data_out = NULL;
++    *size_out = 0;
++
++    /* If the connection object is not available, or there are no NPN
++     * hooks registered, then there's nothing for us to do. */
++    if (c == NULL || sslconn->npn_advertfns == NULL) {
++        return SSL_TLSEXT_ERR_OK;
++    }
++
++    /* Invoke our npn_advertise_protos hook, giving other modules a chance to
++     * add alternate protocol names to advertise. */
++    protos = apr_array_make(c->pool, 0, sizeof(char *));
++    for (i = 0; i < sslconn->npn_advertfns->nelts; i++) {
++        ssl_npn_advertise_protos fn = 
++            APR_ARRAY_IDX(sslconn->npn_advertfns, i, ssl_npn_advertise_protos);
++        
++        if (fn(c, protos) == DONE)
++            break;
++    }
++    num_protos = protos->nelts;
++
++    /* We now have a list of null-terminated strings; we need to concatenate
++     * them together into a single string, where each protocol name is prefixed
++     * by its length.  First, calculate how long that string will be. */
++    size = 0;
++    for (i = 0; i < num_protos; ++i) {
++        const char *string = APR_ARRAY_IDX(protos, i, const char*);
++        unsigned int length = strlen(string);
++        /* If the protocol name is too long (the length must fit in one byte),
++         * then log an error and skip it. */
++        if (length > 255) {
++            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02307)
++                          "SSL NPN protocol name too long (length=%u): %s",
++                          length, string);
++            continue;
++        }
++        /* Leave room for the length prefix (one byte) plus the protocol name
++         * itself. */
++        size += 1 + length;
++    }
++
++    /* If there is nothing to advertise (either because no modules added
++     * anything to the protos array, or because all strings added to the array
++     * were skipped), then we're done. */
++    if (size == 0) {
++        return SSL_TLSEXT_ERR_OK;
++    }
++
++    /* Now we can build the string.  Copy each protocol name string into the
++     * larger string, prefixed by its length. */
++    data = apr_palloc(c->pool, size * sizeof(unsigned char));
++    start = data;
++    for (i = 0; i < num_protos; ++i) {
++        const char *string = APR_ARRAY_IDX(protos, i, const char*);
++        apr_size_t length = strlen(string);
++        if (length > 255)
++            continue;
++        *start = (unsigned char)length;
++        ++start;
++        memcpy(start, string, length * sizeof(unsigned char));
++        start += length;
++    }
++
++    /* Success. */
++    *data_out = data;
++    *size_out = size;
++    return SSL_TLSEXT_ERR_OK;
++}
++
++#endif /* HAVE_TLS_NPN */
+ #ifdef HAVE_SRP
+diff -ru modules/ssl/ssl_private.h modules/ssl/ssl_private.h
+--- modules/ssl/ssl_private.h  2015-04-27 17:41:19.000000000 +0200
++++ modules/ssl/ssl_private.h  2015-04-27 17:45:56.000000000 +0200
+@@ -176,6 +176,11 @@
+ #endif
+ #endif
++/* Next Protocol Negotiation */
++#if !defined(OPENSSL_NO_NEXTPROTONEG) && defined(OPENSSL_NPN_NEGOTIATED)
++#define HAVE_TLS_NPN
++#endif
++
+ /* Secure Remote Password */
+ #if !defined(OPENSSL_NO_SRP) && defined(SSL_CTRL_SET_TLS_EXT_SRP_USERNAME_CB)
+ #define HAVE_SRP
+@@ -448,6 +453,12 @@
+                      * connection */
+     } reneg_state;
++#ifdef HAVE_TLS_NPN
++    /* Poor man's inter-module optional hooks for NPN. */
++    apr_array_header_t *npn_advertfns; /* list of ssl_npn_advertise_protos callbacks */
++    apr_array_header_t *npn_negofns; /* list of ssl_npn_proto_negotiated callbacks. */
++#endif
++
+ #ifdef HAVE_TLS_ALPN
+     /* Poor man's inter-module optional hooks for ALPN. */
+     apr_array_header_t *alpn_proposefns; /* list of ssl_alpn_propose_protos callbacks */
+@@ -634,7 +645,7 @@
+     apr_array_header_t *ssl_ctx_param; /* parameters to pass to SSL_CTX */
+ #endif
+   
+-#ifdef HAVE_TLS_ALPN
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+   apr_array_header_t *ssl_alpn_pref; /* protocol names in order of preference */
+ #endif
+ } modssl_ctx_t;
+@@ -763,7 +774,7 @@
+ const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2);
+ #endif
+-#ifdef HAVE_TLS_ALPN
++#if defined(HAVE_TLS_ALPN) || defined(HAVE_TLS_NPN)
+ const char *ssl_cmd_SSLALPNPreference(cmd_parms *cmd, void *dcfg, const char *protocol);
+ #endif
+@@ -820,6 +831,9 @@
+                              unsigned char *outlen, const unsigned char *in,
+                              unsigned int inlen, void *arg);
+ #endif
++#ifdef HAVE_TLS_NPN
++int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg);
++#endif
+ /**  Session Cache Support  */
+ apr_status_t ssl_scache_init(server_rec *, apr_pool_t *);
diff --git a/modules/http2/sandbox/httpd/patches/openssl-1.0.2-alpn.patch b/modules/http2/sandbox/httpd/patches/openssl-1.0.2-alpn.patch
new file mode 100644 (file)
index 0000000..f433200
--- /dev/null
@@ -0,0 +1,64 @@
+diff -ur openssl-1.0.2c.orig/ssl/t1_lib.c openssl-1.0.2c/ssl/t1_lib.c
+--- openssl-1.0.2c.orig/ssl/t1_lib.c   2015-06-12 16:51:27.000000000 +0200
++++ openssl-1.0.2c/ssl/t1_lib.c        2015-06-23 09:29:51.000000000 +0200
+@@ -1974,6 +1974,8 @@
+ }
+ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p,
++                                       const unsigned char **palpn,
++                                       unsigned short *palpn_len,
+                                        unsigned char *d, int n, int *al)
+ {
+     unsigned short type;
+@@ -2360,7 +2362,7 @@
+ # ifndef OPENSSL_NO_NEXTPROTONEG
+         else if (type == TLSEXT_TYPE_next_proto_neg &&
+                  s->s3->tmp.finish_md_len == 0 &&
+-                 s->s3->alpn_selected == NULL) {
++                 *palpn == NULL) {
+             /*-
+              * We shouldn't accept this extension on a
+              * renegotiation.
+@@ -2384,8 +2386,9 @@
+         else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation &&
+                  s->ctx->alpn_select_cb && s->s3->tmp.finish_md_len == 0) {
+-            if (tls1_alpn_handle_client_hello(s, data, size, al) != 0)
+-                return 0;
++            /* Store extension location for later processing */
++            *palpn = data;
++            *palpn_len = size;
+ # ifndef OPENSSL_NO_NEXTPROTONEG
+             /* ALPN takes precedence over NPN. */
+             s->s3->next_proto_neg_seen = 0;
+@@ -2469,6 +2472,8 @@
+                                  int n)
+ {
+     int al = -1;
++    const unsigned char *alpn = NULL;
++    unsigned short alpn_len = 0;
+     unsigned char *ptmp = *p;
+     /*
+      * Internally supported extensions are parsed first so SNI can be handled
+@@ -2476,7 +2481,7 @@
+      * switch the parent context using SSL_set_SSL_CTX and custom extensions
+      * need to be handled by the new SSL_CTX structure.
+      */
+-    if (ssl_scan_clienthello_tlsext(s, p, d, n, &al) <= 0) {
++    if (ssl_scan_clienthello_tlsext(s, p, &alpn, &alpn_len, d, n, &al) <= 0) {
+         ssl3_send_alert(s, SSL3_AL_FATAL, al);
+         return 0;
+     }
+@@ -2492,6 +2497,12 @@
+         return 0;
+     }
++    if (alpn != NULL) {
++        if (tls1_alpn_handle_client_hello(s, alpn, alpn_len, &al) != 0) {
++            ssl3_send_alert(s, SSL3_AL_FATAL, al);
++            return 0;
++        }
++    }
+     return 1;
+ }
diff --git a/modules/http2/sandbox/httpd/patches/sni_misdirect.patch b/modules/http2/sandbox/httpd/patches/sni_misdirect.patch
new file mode 100644 (file)
index 0000000..5079fc0
--- /dev/null
@@ -0,0 +1,45 @@
+diff -ru ../httpd-2.4.12.orig/include/httpd.h ./include/httpd.h
+--- ../httpd-2.4.12.orig/include/httpd.h       2014-08-22 20:18:08.000000000 +0200
++++ ./include/httpd.h  2015-06-11 17:05:49.000000000 +0200
+@@ -518,6 +518,7 @@
+ #define HTTP_UNSUPPORTED_MEDIA_TYPE          415
+ #define HTTP_RANGE_NOT_SATISFIABLE           416
+ #define HTTP_EXPECTATION_FAILED              417
++#define HTTP_MISDIRECTED_REQUEST             421
+ #define HTTP_UNPROCESSABLE_ENTITY            422
+ #define HTTP_LOCKED                          423
+ #define HTTP_FAILED_DEPENDENCY               424
+diff -ru ../httpd-2.4.12.orig/modules/http/http_protocol.c ./modules/http/http_protocol.c
+--- ../httpd-2.4.12.orig/modules/http/http_protocol.c  2014-10-16 23:42:45.000000000 +0200
++++ ./modules/http/http_protocol.c     2015-06-11 17:04:49.000000000 +0200
+@@ -135,7 +135,7 @@
+     NULL, /* 418 */
+     NULL, /* 419 */
+     NULL, /* 420 */
+-    NULL, /* 421 */
++    "421 Misdirected Request",
+     "422 Unprocessable Entity",
+     "423 Locked",
+     "424 Failed Dependency",
+@@ -1296,6 +1296,9 @@
+     case HTTP_NETWORK_AUTHENTICATION_REQUIRED:
+         return("<p>The client needs to authenticate to gain\n"
+                "network access.</p>\n");
++    case HTTP_MISDIRECTED_REQUEST:
++        return("<p>The client needs to use a new connection for this \n"
++                   "request as it does not match the SNI name used.</p>\n");
+     default:                    /* HTTP_INTERNAL_SERVER_ERROR */
+         /*
+          * This comparison to expose error-notes could be modified to
+diff -ru ../httpd-2.4.12.orig/modules/ssl/ssl_engine_kernel.c ./modules/ssl/ssl_engine_kernel.c
+--- ../httpd-2.4.12.orig/modules/ssl/ssl_engine_kernel.c       2015-06-10 11:46:00.000000000 +0200
++++ ./modules/ssl/ssl_engine_kernel.c  2015-06-11 16:58:34.000000000 +0200
+@@ -203,7 +203,7 @@
+                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02032)
+                             "Hostname %s provided via SNI and hostname %s provided"
+                             " via HTTP are different", servername, host);
+-                return HTTP_BAD_REQUEST;
++                return HTTP_MISDIRECTED_REQUEST;
+             }
+         }
+         else if (((sc->strict_sni_vhost_check == SSL_ENABLED_TRUE)
diff --git a/modules/http2/sandbox/nghttp2/Makefile b/modules/http2/sandbox/nghttp2/Makefile
new file mode 100644 (file)
index 0000000..8cfd700
--- /dev/null
@@ -0,0 +1,239 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+NGHTTP2_VERSION = 1.0.4
+NGHTTP2_DIR     = nghttp2-$(NGHTTP2_VERSION)
+NGHTTP2_TAR     = $(NGHTTP2_DIR).tar.gz
+NGHTTP2_URL     = https://github.com/tatsuhiro-t/nghttp2/releases/download/v$(NGHTTP2_VERSION)/$(NGHTTP2_TAR)
+
+NGHTTP2_CONF_ENV=
+NGHTTP2_DEPS    =
+NGHTTP2_CONF    = --prefix=$(BLD_PREFIX) --enable-app
+
+ZLIB_VERSION    = 1.2.8
+ZLIB_DIR        = zlib-$(ZLIB_VERSION)
+ZLIB_TAR        = $(ZLIB_DIR).tar.gz
+ZLIB_URL        = http://zlib.net/$(ZLIB_TAR)
+
+LIBEV_VERSION    = 4.19
+LIBEV_DIR        = libev-$(LIBEV_VERSION)
+LIBEV_TAR        = $(LIBEV_DIR).tar.gz
+LIBEV_URL        = http://dist.schmorp.de/libev/$(LIBEV_TAR)
+LIBEV_CONF_ENV   += PKG_CONFIG_PATH=$(BLD_PREFIX)/lib/pkgconfig \
+    LDFLAGS=-L$(BLD_PREFIX)/lib CFLAGS=-I$(BLD_PREFIX)/include
+
+LIBEVENT_VERSION = 2.0.22-stable
+LIBEVENT_DIR     = libevent-$(LIBEVENT_VERSION)
+LIBEVENT_TAR     = $(LIBEVENT_DIR).tar.gz
+LIBEVENT_URL     = https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/$(LIBEVENT_TAR)
+LIBEVENT_CONF_ENV += PKG_CONFIG_PATH=$(BLD_PREFIX)/lib/pkgconfig \
+    LDFLAGS=-L$(BLD_PREFIX)/lib CFLAGS=-I$(BLD_PREFIX)/include
+
+GEN             = gen
+INST_DIR        = ../install
+BLD_PREFIX      = $(shell dirname $$PWD)/install
+
+CURL_OPTS       = --progress-bar
+
+OS              = $(shell uname -s)
+
+
+NGHTTP2_CONF_ENV += PKG_CONFIG_PATH=$(BLD_PREFIX)/lib/pkgconfig \
+    LDFLAGS=-L$(BLD_PREFIX)/lib CFLAGS=-I$(BLD_PREFIX)/include
+
+
+# For OS X, we drag our own zlib in as the one installed is not
+# recognized by nghttp2 configure.
+# On other OS, we expect a proper zlib/zlib-dev installation
+#
+ifeq ($(OS),Darwin)
+    # we need our own zlib
+    NGHTTP2_DEPS  += $(INST_DIR)/.zlib-installed $(INST_DIR)/.libev-installed
+else
+ifneq (,$(wildcard $(INST_DIR)/lib/pkgconfig/openssl.pc))
+    NGHTTP2_DEPS  += $(INST_DIR)/.libevent-installed
+endif
+endif
+
+all: install
+
+dirs:
+       @mkdir -p $(GEN)/build
+
+clean:
+       @rm -rf $(GEN)/$(NGHTTP2_DIR)
+
+distclean:
+       @rm -rf $(GEN)
+
+distdir:
+       @mkdir -p $(distdir)
+       @tar cf - Makefile patches | (cd $(distdir) && tar xf - )
+
+install:  $(INST_DIR)/.nghttp2-installed
+
+
+################################################################################
+# Install the local nghttp2
+#
+$(INST_DIR)/.nghttp2-installed:  $(GEN)/$(NGHTTP2_DIR)/.nghttp2-built
+       @echo -n installing nghttp2 locally...
+       @cd $(GEN)/$(NGHTTP2_DIR)/ && make install >> ../build.log
+       @echo done.
+       @touch $(INST_DIR)/.nghttp2-installed
+
+
+################################################################################
+# Build the local nghttp2
+#
+$(GEN)/$(NGHTTP2_DIR)/.nghttp2-built: \
+               $(GEN)/$(NGHTTP2_DIR)/.nghttp2-configured
+       @echo -n building nghttp2...
+       @cd $(GEN)/$(NGHTTP2_DIR)/ && make >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(NGHTTP2_DIR)/.nghttp2-built
+
+################################################################################
+# Configure the local nghttp2 sources
+#
+$(GEN)/$(NGHTTP2_DIR)/.nghttp2-configured: \
+               $(NGHTTP2_DEPS) \
+               $(GEN)/$(NGHTTP2_DIR)/.nghttp2-patched
+       @echo -n configuring nghttp2...
+       cd $(GEN)/$(NGHTTP2_DIR)/ && \
+       $(NGHTTP2_CONF_ENV) ./configure $(NGHTTP2_CONF) >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(NGHTTP2_DIR)/.nghttp2-configured
+
+################################################################################
+# Patch the local nghtp2 sources
+#
+$(GEN)/$(NGHTTP2_DIR)/.nghttp2-patched: \
+               $(GEN)/$(NGHTTP2_DIR)/.nghttp2-extracted
+       @touch $(GEN)/$(NGHTTP2_DIR)/.nghttp2-patched
+
+################################################################################
+# Extract nghttp2 source tree
+#
+$(GEN)/$(NGHTTP2_DIR)/.nghttp2-extracted: \
+               $(GEN)/$(NGHTTP2_TAR)
+       @rm -rf $(GEN)/$(NGHTTP2_DIR)
+       @echo -n extracting nghttp2 packages...
+       @cd $(GEN) && tar xfz $(NGHTTP2_TAR)
+       @echo done.
+       @touch $(GEN)/$(NGHTTP2_DIR)/.nghttp2-extracted
+
+################################################################################
+# Retrieve nghttp2 sources
+#
+$(GEN)/$(NGHTTP2_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) -L $(NGHTTP2_URL) > $(GEN)/$(NGHTTP2_TAR)
+
+
+################################################################################
+# Build + install a local libevent library (if needed)
+#
+$(INST_DIR)/.libevent-installed: \
+               $(GEN)/$(LIBEVENT_DIR)/.libevent-built
+       @echo -n installing libevent locally...
+       @cd $(GEN)/$(LIBEVENT_DIR) && make install >> ../build.log
+       @echo done.
+       @touch $(INST_DIR)/.libevent-installed
+
+$(GEN)/$(LIBEVENT_DIR)/.libevent-built: \
+               $(GEN)/$(LIBEVENT_DIR)/.libevent-configured
+       @echo -n building libevent locally...
+       @cd $(GEN)/$(LIBEVENT_DIR) && make >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(LIBEVENT_DIR)/.libevent-built
+
+$(GEN)/$(LIBEVENT_DIR)/.libevent-configured: \
+               $(GEN)/$(LIBEVENT_DIR)/.libevent-extracted
+       @echo -n configuring libevent...
+       @cd $(GEN)/$(LIBEVENT_DIR) && $(LIBEVENT_CONF_ENV) ./configure --prefix=$(BLD_PREFIX) --with-sysroot=$(BLD_PREFIX)
+       @echo done.
+       @touch $(GEN)/$(LIBEVENT_DIR)/.libevent-configured
+
+$(GEN)/$(LIBEVENT_DIR)/.libevent-extracted: \
+        $(GEN)/$(LIBEVENT_TAR)
+       @rm -rf $(GEN)/$(LIBEVENT_DIR)
+       @echo -n downloading and extracting libevent...
+       @cd gen; tar xfz $(LIBEVENT_TAR)
+       @echo done.
+       @touch $(GEN)/$(LIBEVENT_DIR)/.libevent-extracted
+
+$(GEN)/$(LIBEVENT_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) -L $(LIBEVENT_URL) > $(GEN)/$(LIBEVENT_TAR)
+
+################################################################################
+# Build + install a local libev library (if needed)
+#
+$(INST_DIR)/.libev-installed: \
+               $(GEN)/$(LIBEV_DIR)/.libev-built
+       @echo -n installing libev locally...
+       @cd $(GEN)/$(LIBEV_DIR) && make install >> ../build.log
+       @echo done.
+       @touch $(INST_DIR)/.libev-installed
+
+$(GEN)/$(LIBEV_DIR)/.libev-built: \
+               $(GEN)/$(LIBEV_DIR)/.libev-configured
+       @echo -n building libev locally...
+       @cd $(GEN)/$(LIBEV_DIR) && make >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(LIBEV_DIR)/.libev-built
+
+$(GEN)/$(LIBEV_DIR)/.libev-configured: $(GEN)/$(LIBEV_TAR)
+       @rm -rf $(GEN)/$(LIBEV_DIR)
+       @cd $(GEN) && tar xfz $(LIBEV_TAR)
+       @echo -n configuring libev...
+       @cd $(GEN)/$(LIBEV_DIR) && $(LIBEV_CONF_ENV) ./configure --prefix=$(BLD_PREFIX)
+       @echo done.
+       @touch $(GEN)/$(LIBEV_DIR)/.libev-configured
+
+$(GEN)/$(LIBEV_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) -L $(LIBEV_URL) > $(GEN)/$(LIBEV_TAR)
+
+################################################################################
+# Build + install a local zlib library (if needed)
+#
+$(INST_DIR)/.zlib-installed: \
+               $(GEN)/$(ZLIB_DIR)/.zlib-built
+       @echo -n installing zlib locally...
+       @cd $(GEN)/$(ZLIB_DIR) && make install >> ../build.log
+       @echo done.
+       @touch $(INST_DIR)/.zlib-installed
+
+$(GEN)/$(ZLIB_DIR)/.zlib-built: \
+               $(GEN)/$(ZLIB_DIR)/.zlib-configured
+       @echo -n building zlib locally...
+       @cd $(GEN)/$(ZLIB_DIR) && make >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(ZLIB_DIR)/.zlib-built
+
+$(GEN)/$(ZLIB_DIR)/.zlib-configured: $(GEN)/$(ZLIB_TAR)
+       @rm -rf $(GEN)/$(ZLIB_DIR)
+       @cd $(GEN) && tar xfz $(ZLIB_TAR)
+       @echo -n configuring zlib...
+       @cd $(GEN)/$(ZLIB_DIR) && ./configure --prefix=$(BLD_PREFIX)
+       @echo done.
+       @touch $(GEN)/$(ZLIB_DIR)/.zlib-configured
+
+$(GEN)/$(ZLIB_TAR):
+       @mkdir -p $(GEN)
+       curl $(CURL_OPTS) -L $(ZLIB_URL) > $(GEN)/$(ZLIB_TAR)
+
diff --git a/modules/http2/sandbox/test/Makefile b/modules/http2/sandbox/test/Makefile
new file mode 100644 (file)
index 0000000..32100e7
--- /dev/null
@@ -0,0 +1,323 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+HOST_NAME    = test.example.org
+HOST2_NAME   = test-ser.example.org
+
+HTTP_PORT    = 12345
+HTTPS_PORT   = 12346
+
+HTTP_AUTH    = $(HOST_NAME):$(HTTP_PORT)
+HTTPS_AUTH   = $(HOST_NAME):$(HTTPS_PORT)
+HTTPS_AUTH2  = $(HOST2_NAME):$(HTTPS_PORT)
+
+GEN          = gen
+INST_DIR     = ../install
+BLD_PREFIX   = $(shell dirname $$PWD)/install
+
+SUB_DIRS     = clients
+
+CURL         = $(INST_DIR)/bin/curl
+NGHTTP       = $(INST_DIR)/bin/nghttp
+H2LOAD       = $(INST_DIR)/bin/h2load
+
+PHP_CGI      = $(shell bash -c 'type -p php-cgi')
+
+all: install
+
+clean:
+       @rm -rf $(GEN) $(INST_DIR)/.testdocs-setup $(INST_DIR)/.test-setup
+       @make -C clients clean
+
+distclean:
+       @rm -rf $(GEN)
+       $(foreach sd, $(SUB_DIRS), make -C $(sd) distclean; )
+
+distdir:
+       @mkdir -p $(distdir)
+       @tar cf - Makefile bin conf htdocs clients/Makefile *.txt *.sh | (cd $(distdir) && tar xf - )
+       @rm -f $(distdir)/conf/ssl/mod-h2.greebytes.de*
+       @rm -f $(distdir)/conf/sites/mod-h2.greebytes.de.conf
+
+start: $(INST_DIR)/.test-setup
+       @$(INST_DIR)/bin/apachectl -k start
+
+start-debug: $(INST_DIR)/.test-setup
+       @$(INST_DIR)/bin/apachectl -k start -X
+
+restart: $(INST_DIR)/.test-setup
+       @echo -n shutting down httpd...
+       @$(INST_DIR)/bin/apachectl -k stop
+       @while true; do sleep 1; pkill -1 -U $(USER) httpd; test "$$?" != "0" && break; done   
+       @echo done.
+       @$(INST_DIR)/bin/apachectl -k restart
+
+stop:
+       @$(INST_DIR)/bin/apachectl -k stop
+
+
+install:
+       @make -C clients install
+
+################################################################################
+# Test the local httpd
+#
+test: \
+               $(INST_DIR)/.test-setup \
+               $(INST_DIR)/.curl-installed
+       @rm -rf $(GEN)/tmp
+       @mkdir -p $(GEN)/tmp
+       @$(INST_DIR)/bin/apachectl restart
+       @bash test_curl_altsvc.sh http://$(HTTP_AUTH) https://$(HTTPS_AUTH)
+       @bash test_alt_host.sh https://$(HTTPS_AUTH)
+       @bash test_nghttp_get.sh  https://$(HTTPS_AUTH)
+       @bash test_nghttp_post.sh https://$(HTTPS_AUTH)
+       @bash test_curl_get.sh    https://$(HTTPS_AUTH)
+       @bash test_curl_post.sh   https://$(HTTPS_AUTH)
+       @bash test_nghttp_get.sh  https://$(HTTPS_AUTH)/proxy
+       @bash test_nghttp_post.sh https://$(HTTPS_AUTH)/proxy
+       @bash test_curl_get.sh    https://$(HTTPS_AUTH)/proxy
+       @bash test_curl_post.sh   https://$(HTTPS_AUTH)/proxy
+       @bash test_nghttp_get.sh  https://$(HTTPS_AUTH)/rewrite
+       @bash test_nghttp_post.sh https://$(HTTPS_AUTH)/rewrite
+       @bash test_curl_get.sh    https://$(HTTPS_AUTH)/rewrite
+       @bash test_curl_post.sh   https://$(HTTPS_AUTH)/rewrite
+       @bash test_nghttp_get.sh  http://$(HTTP_AUTH)
+       @bash test_nghttp_post.sh http://$(HTTP_AUTH)
+       @bash test_curl_get.sh    http://$(HTTP_AUTH)
+       @bash test_curl_post.sh   http://$(HTTP_AUTH)
+       @bash test_nghttp_get.sh  http://$(HTTP_AUTH) direct
+       @bash test_nghttp_post.sh http://$(HTTP_AUTH) direct
+       @bash test_nghttp_get.sh  http://$(HTTP_AUTH)/proxy
+       @bash test_nghttp_post.sh http://$(HTTP_AUTH)/proxy
+       @bash test_curl_get.sh    http://$(HTTP_AUTH)/proxy
+       @bash test_curl_post.sh   http://$(HTTP_AUTH)/proxy
+       @bash test_nghttp_get.sh  http://$(HTTP_AUTH)/rewrite
+       @bash test_nghttp_post.sh http://$(HTTP_AUTH)/rewrite
+       @bash test_curl_get.sh    http://$(HTTP_AUTH)/rewrite
+       @bash test_curl_post.sh   http://$(HTTP_AUTH)/rewrite
+       @bash test_nghttp_get.sh  https://$(HTTPS_AUTH2)
+       @bash test_nghttp_post.sh https://$(HTTPS_AUTH2)
+       @bash test_curl_get.sh    https://$(HTTPS_AUTH2)
+       @bash test_curl_post.sh   https://$(HTTPS_AUTH2)
+
+################################################################################
+# Load Test
+#
+
+$(GEN)/load-urls-1.txt: \
+               load-urls-1.txt
+       @mkdir -p $(GEN)/tmp
+       @sed -e "s,SUBST_AUTH_SUBST,https://$(HTTPS_AUTH)," < load-urls-1.txt >$(GEN)/load-urls-1.txt
+
+$(GEN)/load-urls-2.txt: \
+               load-urls-1.txt
+       @mkdir -p $(GEN)/tmp
+       @sed -e "s,SUBST_AUTH_SUBST,http://$(HTTP_AUTH)," < load-urls-1.txt >$(GEN)/load-urls-2.txt
+
+MAX_STREAMS = 100
+
+loadtest: \
+               $(INST_DIR)/.test-setup \
+               $(INST_DIR)/.curl-installed \
+        $(GEN)/load-urls-1.txt
+       $(H2LOAD) -c 100 -t 10 -n 100000 -m $(MAX_STREAMS) https://$(HTTPS_AUTH)/index.html
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 1 -m $(MAX_STREAMS) -c 8
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 2 -m $(MAX_STREAMS) -c 8
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 3 -m $(MAX_STREAMS) -c 8
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 4 -m $(MAX_STREAMS) -c 8
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 5 -m $(MAX_STREAMS) -c 8
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 6 -m $(MAX_STREAMS) -c 8
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 7 -m $(MAX_STREAMS) -c 8
+       $(H2LOAD) -i $(GEN)/load-urls-1.txt -n 200000 -t 8 -m $(MAX_STREAMS) -c 8
+
+xtest: \
+               $(INST_DIR)/.test-setup \
+               $(INST_DIR)/.curl-installed \
+        $(GEN)/load-urls-1.txt
+       @bash test_nghttp_post.sh https://$(HTTPS_AUTH)
+
+################################################################################
+# Switch mpm modules
+#
+mpm_worker: $(INST_DIR)/.testdocs-setup
+       @test -L $(INST_DIR)/conf/mods-enabled/mpm_worker.load || \
+        (rm -f $(INST_DIR)/conf/mods-enabled/mpm_*; \
+        ln -s ../mods-available/mpm_worker.load $(INST_DIR)/conf/mods-enabled/ && \
+        echo "mpm_worker configured, please restart.")
+
+mpm_event: $(INST_DIR)/.testdocs-setup
+       @test -L $(INST_DIR)/conf/mods-enabled/mpm_event.load || \
+           (rm -f $(INST_DIR)/conf/mods-enabled/mpm_*; \
+           ln -s ../mods-available/mpm_event.load $(INST_DIR)/conf/mods-enabled/ && \
+           echo "mpm_event configured, please restart.")
+
+mpm_prefork: $(INST_DIR)/.testdocs-setup
+       @test -L $(INST_DIR)/conf/mods-enabled/mpm_prefork.load || \
+           (rm -f $(INST_DIR)/conf/mods-enabled/mpm_*; \
+           ln -s ../mods-available/mpm_prefork.load $(INST_DIR)/conf/mods-enabled/ && \
+           echo "mpm_prefork configured, please restart.")
+
+
+################################################################################
+# Setup the local httpd for our tests
+#
+SETUP_FILES = $(wildcard conf/*.conf) \
+    $(wildcard conf/mods-available/*.*) \
+    $(wildcard conf/sites/*.conf) \
+    $(wildcard bin/*)
+
+$(INST_DIR)/.testdocs-setup: \
+       $(wildcard conf/*) \
+       $(wildcard htdocs/*/*)
+       @echo -n setup htdocs locally...
+       @cp -r htdocs/* $(INST_DIR)/htdocs
+       @chmod +x $$(find $(INST_DIR)/htdocs/test.example.org/ -name "*.py" )
+       @echo done.
+       @touch $(INST_DIR)/.testdocs-setup
+
+$(INST_DIR)/.test-setup: \
+        Makefile \
+               $(INST_DIR)/.httpd-installed \
+               conf/*.conf \
+        $(wildcard bin/*) \
+        $(wildcard conf/mods-available/*.*) \
+               $(wildcard conf/sites/*.conf) \
+        $(INST_DIR)/conf/mods-enabled/h2.load \
+               $(INST_DIR)/conf/ssl/test.example.org.key \
+               $(INST_DIR)/conf/ssl/test.example.org.pem \
+               $(INST_DIR)/conf/ssl/test-ser.example.org.key \
+               $(INST_DIR)/conf/ssl/test-ser.example.org.pem \
+               $(INST_DIR)/conf/ssl/noh2.example.org.key \
+               $(INST_DIR)/conf/ssl/noh2.example.org.pem \
+               $(INST_DIR)/.testdocs-setup
+       @echo -n setup httpd locally...
+       @mkdir -p $(INST_DIR)/conf/sites
+       @mkdir -p $(INST_DIR)/conf/mods-enabled
+       @mkdir -p $(INST_DIR)/conf/ssl
+       @for file in $(SETUP_FILES); do \
+               sed -e "s,SUBST_SERVER_ROOT_SUBST,$(BLD_PREFIX),g" \
+                       -e "s,SUBST_SERVER_NAME_SUBST,$(shell hostname -f),g" \
+                       -e "s,SUBST_PORT_HTTP_SUBST,$(HTTP_PORT),g" \
+                       -e "s,SUBST_PORT_HTTPS_SUBST,$(HTTPS_PORT),g" \
+            -e "s,SUBST_PHP_CGI_SUBST,$(PHP_CGI),g" \
+                       < $$file > $(INST_DIR)/$$file; \
+       done
+       @for i in bin/*; do chmod +x $(INST_DIR)/$$i; done
+       @cp conf/ssl/*.* $(INST_DIR)/conf/ssl
+       @test -L $(INST_DIR)/conf/mods-enabled/mpm*load || ln -s ../mods-available/mpm_worker.load $(INST_DIR)/conf/mods-enabled/
+       @echo done.
+       @touch $(INST_DIR)/.test-setup
+
+$(INST_DIR)/conf/ssl/test.example.org.key:
+       @mkdir -p $(INST_DIR)/conf/ssl
+       openssl genrsa -out $(INST_DIR)/conf/ssl/test.example.org.key 4096
+
+$(INST_DIR)/conf/ssl/test.example.org.req: \
+               $(INST_DIR)/conf/ssl/test.example.org.key \
+        conf/ssl/test.example.org.x509.input
+       openssl req -new -key $(INST_DIR)/conf/ssl/test.example.org.key \
+               -out $(INST_DIR)/conf/ssl/test.example.org.req < conf/ssl/test.example.org.x509.input
+
+$(INST_DIR)/conf/ssl/test.example.org.x509.input: \
+        conf/ssl/test.example.org.x509.input
+       cp conf/ssl/test.example.org.x509.input $(INST_DIR)/conf/ssl
+
+$(INST_DIR)/conf/ssl/test.example.org.pem: \
+               $(INST_DIR)/conf/ssl/test.example.org.key \
+               $(INST_DIR)/conf/ssl/test.example.org.req \
+               conf/ssl/extensions.conf \
+               conf/ssl/test.example.org.x509.input
+       openssl x509 -req -sha256 -days 1095 \
+               -signkey $(INST_DIR)/conf/ssl/test.example.org.key \
+               -in $(INST_DIR)/conf/ssl/test.example.org.req \
+               -out $(INST_DIR)/conf/ssl/test.example.org.pem \
+               -extfile conf/ssl/extensions.conf -extensions ssl_client < conf/ssl/noh2.example.org.x509.input
+
+$(INST_DIR)/conf/ssl/test-ser.example.org.key:
+       @mkdir -p $(INST_DIR)/conf/ssl
+       openssl genrsa -out $(INST_DIR)/conf/ssl/test-ser.example.org.key 4096
+
+$(INST_DIR)/conf/ssl/test-ser.example.org.x509.input: \
+        conf/ssl/test-ser.example.org.x509.input
+       cp conf/ssl/test-ser.example.org.x509.input $(INST_DIR)/conf/ssl
+
+$(INST_DIR)/conf/ssl/test-ser.example.org.req: \
+               $(INST_DIR)/conf/ssl/test-ser.example.org.key
+       openssl req -new -key $(INST_DIR)/conf/ssl/test-ser.example.org.key \
+               -out $(INST_DIR)/conf/ssl/test-ser.example.org.req < conf/ssl/test-ser.example.org.x509.input
+
+$(INST_DIR)/conf/ssl/test-ser.example.org.pem: \
+               $(INST_DIR)/conf/ssl/test-ser.example.org.key \
+               $(INST_DIR)/conf/ssl/test-ser.example.org.req \
+               conf/ssl/extensions.conf \
+               conf/ssl/test-ser.example.org.x509.input
+       openssl x509 -req -sha256 -days 1095 \
+               -signkey $(INST_DIR)/conf/ssl/test-ser.example.org.key \
+               -in $(INST_DIR)/conf/ssl/test-ser.example.org.req \
+               -out $(INST_DIR)/conf/ssl/test-ser.example.org.pem \
+               -extfile conf/ssl/extensions.conf -extensions ssl_client < conf/ssl/noh2.example.org.x509.input
+
+$(INST_DIR)/conf/ssl/noh2.example.org.key:
+       @mkdir -p $(INST_DIR)/conf/ssl
+       openssl genrsa -out $(INST_DIR)/conf/ssl/noh2.example.org.key 4096
+
+$(INST_DIR)/conf/ssl/noh2.example.org.x509.input: \
+        conf/ssl/noh2.example.org.x509.input
+       cp conf/ssl/noh2.example.org.x509.input $(INST_DIR)/conf/ssl
+
+$(INST_DIR)/conf/ssl/noh2.example.org.req: \
+               $(INST_DIR)/conf/ssl/noh2.example.org.key
+       openssl req -new -key $(INST_DIR)/conf/ssl/noh2.example.org.key \
+               -out $(INST_DIR)/conf/ssl/noh2.example.org.req < conf/ssl/noh2.example.org.x509.input
+
+$(INST_DIR)/conf/ssl/noh2.example.org.pem: \
+               $(INST_DIR)/conf/ssl/noh2.example.org.key \
+               $(INST_DIR)/conf/ssl/noh2.example.org.req \
+               conf/ssl/extensions.conf \
+               conf/ssl/noh2.example.org.x509.input
+       openssl x509 -req -sha256 -days 1095 \
+               -signkey $(INST_DIR)/conf/ssl/noh2.example.org.key \
+               -in $(INST_DIR)/conf/ssl/noh2.example.org.req \
+               -out $(INST_DIR)/conf/ssl/noh2.example.org.pem \
+               -extfile conf/ssl/extensions.conf -extensions ssl_client < conf/ssl/noh2.example.org.x509.input
+
+$(INST_DIR)/conf/mods-available/h2.load:
+       @mkdir -p $(INST_DIR)/conf/mods-available
+       cp ../../setup/h2.load $(INST_DIR)/conf/mods-available
+
+$(INST_DIR)/conf/mods-available/h2.conf:
+       @mkdir -p $(INST_DIR)/conf/mods-available
+       cp ../../setup/h2.conf $(INST_DIR)/conf/mods-available
+
+$(INST_DIR)/conf/mods-enabled/h2.load: \
+        $(INST_DIR)/conf/mods-available/h2.load \
+        $(INST_DIR)/conf/mods-available/h2.conf
+       @mkdir -p $(INST_DIR)/conf/mods-enabled
+       @cd $(INST_DIR)/conf/mods-enabled && ln -sf ../mods-available/h2.* .
+
+
+################################################################################
+# Install the local httpd for our tests
+#
+$(INST_DIR)/.httpd-installed:
+       @echo "you need to have httpd installed locally."
+       @exit 1
+
+################################################################################
+# Install the local curl
+#
+$(INST_DIR)/.curl-installed:
+       make -C clients
diff --git a/modules/http2/sandbox/test/bin/php-wrapper b/modules/http2/sandbox/test/bin/php-wrapper
new file mode 100644 (file)
index 0000000..85baaf8
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Set desired PHP_FCGI_* environment variables.
+# Example:
+# PHP FastCGI processes exit after 500 requests by default.
+PHP_FCGI_MAX_REQUESTS=10000
+export PHP_FCGI_MAX_REQUESTS
+
+# set via Makefile
+PHP_CGI=SUBST_PHP_CGI_SUBST
+
+exec ${PHP_CGI:-php-cgi}
diff --git a/modules/http2/sandbox/test/bin/testrun b/modules/http2/sandbox/test/bin/testrun
new file mode 100644 (file)
index 0000000..5da81f1
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/bash
+#
+#
+#
+
+
+SCHEME=https
+HOST=test.example.org
+PORT=12346
+
+URL=$SCHEME://$HOST:$PORT
+
+WRK=../wrk/wrk
+H2LOAD=${H2LOAD:-sandbox/install/bin/h2load}
+
+run_wrk() {
+    path=$1
+    $WRK -c100 -t${THREADS:-8} -d30s $URL$path >/tmp/$$.out 2>&1
+
+    stat=$( fgrep 'Requests/sec: ' /tmp/$$.out )
+    reqs=${stat##Requests/sec: }
+}
+
+run_load() {
+    path=$1
+    $H2LOAD -c 100 -t ${THREADS:-8} -m ${MAX_STREAMS:-1} -n 500000 $URL$path > /tmp/$$.out 2>&1
+
+    fin=$( fgrep 'finished in ' /tmp/$$.out )
+    stat=$( fgrep 'requests: ' /tmp/$$.out )
+
+    case "$stat" in
+        *", 0 failed,"*)
+            # ok
+            reqs=${fin#*, }
+            reqs=${reqs%% req/s,*}
+            mbs=${fin##*, }
+            ;;
+        *)
+            reqs="NaN ($stat)"
+            mbs="NaN ($stat)"
+            ;;
+    esac
+
+}
+
+run_m() {
+    path=$1
+    iterations="$2"
+    for m in $iterations; do
+        MAX_STREAMS=$m
+        run_load $path
+        echo -n ", $reqs"
+    done
+}
+
+run_iter() {
+    path=$1
+    max=$2
+    iterations=$3
+    echo -n "m, wrk"
+    for i in $iterations; do
+        echo -n ", $i"
+    done
+    echo ""
+    i=1
+    while [ $i -le $max ]; do
+        echo -n "$i"
+        run_wrk $path
+        echo -n ", $reqs"
+        run_m $path "$iterations"
+        echo ""
+        i=$[ i + 1 ]
+    done
+}
+
+URL_PATH=$1
+
+echo "$URL_PATH, 500k requests, req/s"
+run_iter $URL_PATH 10 "1 2 3 4 5 6 7 8 9 10 15 20 40 100"
diff --git a/modules/http2/sandbox/test/clients/Makefile b/modules/http2/sandbox/test/clients/Makefile
new file mode 100644 (file)
index 0000000..e4f13d6
--- /dev/null
@@ -0,0 +1,113 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+CURL_VERSION = 7.43.0
+CURL_DIR     = curl-$(CURL_VERSION)
+CURL_TAR     = $(CURL_DIR).tar.gz
+CURL_URL     = http://curl.haxx.se/download/$(CURL_TAR)
+
+GEN          = gen
+INST_DIR     = ../../install
+BLD_PREFIX   = $(shell dirname $(shell dirname $$PWD))/install
+
+OS           = $(shell uname -s)
+
+CURL_CONF    = --with-nghttp2=$(BLD_PREFIX)
+
+OPENSSL_VERSION = $(shell openssl version -v | sed -e 's/OpenSSL *//g' -e 's/[a-z]* .*//g')
+
+ifeq ($(OPENSSL_VERSION), $(filter $(OPENSSL_VERSION),0.9.7 0.9.8 1.0.0))
+       # Very old openssl without alpn or npn support installed, need a newer one
+    CURL_CONF   += --with-ssl=$(BLD_PREFIX)
+endif
+
+all: install-curl
+
+dirs:
+       @mkdir -p $(GEN)/build
+
+clean:
+       @rm -rf $(GEN)/$(CURL_DIR) $(GEN)/build.log
+
+distclean:
+       @rm -rf $(GEN)
+
+install: install-curl
+
+install-curl: $(INST_DIR)/.curl-installed
+
+################################################################################
+# Install the local curl our tests
+#
+$(INST_DIR)/.curl-installed:  $(GEN)/$(CURL_DIR)/.curl-built
+       @echo -n installing curl locally...
+       @cd $(GEN)/$(CURL_DIR)/ && make install >> ../build.log
+       @echo done.
+       @touch $(INST_DIR)/.curl-installed
+
+
+################################################################################
+# Build the local curl
+#
+$(GEN)/$(CURL_DIR)/.curl-built: \
+               $(GEN)/$(CURL_DIR)/.curl-configured
+       @echo -n building curl...
+       @cd $(GEN)/$(CURL_DIR)/ && make >> ../build.log
+       @echo done.
+       @touch $(GEN)/$(CURL_DIR)/.curl-built
+
+################################################################################
+# Configure the local curl sources
+#
+
+PKG_CONF_MSG = Unable to find pkg-config executable. You need pkg-config installed \
+for curl to be built with nghttp2. On OS X try: \"brew install pkg-config\" \
+on Linux you local package manager should be able to help.
+
+$(GEN)/$(CURL_DIR)/.curl-configured: \
+               $(CURL_DEPS) \
+        $(INST_DIR)/.nghttp2-installed \
+               $(GEN)/$(CURL_DIR)/.curl-extracted
+       @echo -n configuring curl...
+       if test ! -x $$( type -p pkg-config ); then echo $(PKG_CONF_MSG); exit 1; fi
+       cd $(GEN)/$(CURL_DIR)/ && \
+       PKG_CONFIG_LIBDIR=$(BLD_PREFIX)/lib/pkgconfig ./configure --prefix=$(BLD_PREFIX) $(CURL_CONF)
+       @echo done.
+       @touch $(GEN)/$(CURL_DIR)/.curl-configured
+
+################################################################################
+# Extract curl source tree
+#
+$(GEN)/$(CURL_DIR)/.curl-extracted: \
+               $(GEN)/$(CURL_TAR)
+       @rm -rf $(GEN)/$(CURL_DIR)
+       @echo -n extracting curl packages...
+       @cd $(GEN) && tar xfz $(CURL_TAR)
+       @echo done.
+       @touch $(GEN)/$(CURL_DIR)/.curl-extracted
+
+################################################################################
+# Retrieve curl sources, we use curl for that???
+#
+$(GEN)/$(CURL_TAR):
+       @mkdir -p $(GEN)
+       curl --progress-bar $(CURL_URL) > $(GEN)/$(CURL_TAR)
+
+
+################################################################################
+# build and install local nghttp2
+#
+$(INST_DIR)/.nghttp2-installed:
+       @make -C ../nghttp2 install
diff --git a/modules/http2/sandbox/test/conf/httpd.conf b/modules/http2/sandbox/test/conf/httpd.conf
new file mode 100644 (file)
index 0000000..746bd65
--- /dev/null
@@ -0,0 +1,120 @@
+################################################################################
+#
+# Test Setup for localhost
+#
+################################################################################
+
+ServerName SUBST_SERVER_NAME_SUBST
+ServerRoot "SUBST_SERVER_ROOT_SUBST"
+
+Listen SUBST_PORT_HTTP_SUBST
+
+ServerName localhost
+
+Include conf/modules.conf
+
+IncludeOptional conf/mods-enabled/*.load
+IncludeOptional conf/mods-enabled/*.conf
+
+ServerAdmin you@localhost
+
+<Directory />
+    AllowOverride none
+    Require all denied
+</Directory>
+
+DocumentRoot "SUBST_SERVER_ROOT_SUBST/htdocs"
+<Directory "SUBST_SERVER_ROOT_SUBST/htdocs">
+    Options Indexes FollowSymLinks
+    AllowOverride None
+    Require all granted
+</Directory>
+
+<IfModule dir_module>
+    DirectoryIndex index.html
+    DirectoryIndex index.php
+</IfModule>
+<Files ".ht*">
+    Require all denied
+</Files>
+
+ErrorLog "logs/error_log"
+LogLevel warn
+
+CoreDumpDirectory "/tmp"
+
+<IfModule log_config_module>
+    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+    LogFormat "%h %l %u %t \"%r\" %>s %b" common
+    CustomLog "logs/access_log" common
+</IfModule>
+
+<IfModule alias_module>
+    ScriptAlias /cgi-bin/ "SUBST_SERVER_ROOT_SUBST/cgi-bin/"
+</IfModule>
+<Directory "SUBST_SERVER_ROOT_SUBST/cgi-bin">
+    AllowOverride None
+    Options None
+    Require all granted
+</Directory>
+
+<IfModule mime_module>
+    TypesConfig conf/mime.types
+    AddType application/x-compress .Z
+</IfModule>
+
+
+<IfModule h2_module>
+    LogLevel h2:info
+    H2Engine on
+
+    # max parallel streams per session, default 100
+    #H2MaxSessionStreams
+    # max initial window size upstream (per stream), default 64k
+    #H2InitialWindowSize 
+    # max header list size (the headers for a stream), default 16k
+    #H2MaxHeaderListSize
+    # max memory buffer size downstream (per stream), default 64k
+    #H2StreamMaxMemSize
+
+    # you may set these explicitly, otherwise they are taking from
+    # ThreadsPerChild and HardLimitThreads parameters of the mpm module
+    #
+    #H2MinWorkers 8
+    #H2MaxWorkers 128
+    # seconds a worker stays idle until shut down, default 10 minutes
+    #H2MaxWorkerIdleSeconds 600
+
+    # If stream processing should serialize/read HTTP/1.1 headers for 
+    # better backward compatibility with eventually installed filters
+    # from other modules or changes in the core.
+    # default is off
+    #H2SerializeHeaders Off
+
+    # When mpm_event is selected, perform a hack on internal connections that
+    # allow them to work. Has no effect when other mpm modules are selected.
+    # Defaults to "On"
+    #H2HackMpmEvent On
+
+</IfModule>
+
+
+################################################################################
+# SSL Setup
+################################################################################
+<IfModule ssl_module>
+       SSLCipherSuite HIGH:!aNULL:!MD5
+       SSLProtocol All -SSLv2 -SSLv3
+       SSLPassPhraseDialog  builtin
+       SSLSessionCache        "shmcb:SUBST_SERVER_ROOT_SUBST/logs/ssl_scache(512000)"
+       SSLSessionCacheTimeout  300
+
+       SSLRandomSeed startup builtin
+       SSLRandomSeed connect builtin
+
+       Listen SUBST_PORT_HTTPS_SUBST
+
+</IfModule>
+
+IncludeOptional conf/sites/*.conf
+
diff --git a/modules/http2/sandbox/test/conf/mods-available/mpm_event.load b/modules/http2/sandbox/test/conf/mods-available/mpm_event.load
new file mode 100644 (file)
index 0000000..6b0f8e2
--- /dev/null
@@ -0,0 +1 @@
+LoadModule mpm_event_module modules/mod_mpm_event.so
diff --git a/modules/http2/sandbox/test/conf/mods-available/mpm_prefork.load b/modules/http2/sandbox/test/conf/mods-available/mpm_prefork.load
new file mode 100644 (file)
index 0000000..f10fd42
--- /dev/null
@@ -0,0 +1 @@
+LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
diff --git a/modules/http2/sandbox/test/conf/mods-available/mpm_worker.load b/modules/http2/sandbox/test/conf/mods-available/mpm_worker.load
new file mode 100644 (file)
index 0000000..21bd173
--- /dev/null
@@ -0,0 +1 @@
+LoadModule mpm_worker_module modules/mod_mpm_worker.so
diff --git a/modules/http2/sandbox/test/conf/modules.conf b/modules/http2/sandbox/test/conf/modules.conf
new file mode 100644 (file)
index 0000000..ea92028
--- /dev/null
@@ -0,0 +1,99 @@
+#
+# Dynamic Shared Object (DSO) Support
+#
+# To be able to use the functionality of a module which was built as a DSO you
+# have to place corresponding `LoadModule' lines at this location so the
+# directives contained in it are actually available _before_ they are used.
+# Statically compiled modules (those listed by `httpd -l') do not need
+# to be loaded here.
+#
+# Example:
+# LoadModule foo_module modules/mod_foo.so
+#
+
+LoadModule authn_file_module modules/mod_authn_file.so
+#LoadModule authn_dbm_module modules/mod_authn_dbm.so
+#LoadModule authn_anon_module modules/mod_authn_anon.so
+#LoadModule authn_dbd_module modules/mod_authn_dbd.so
+#LoadModule authn_socache_module modules/mod_authn_socache.so
+LoadModule authn_core_module modules/mod_authn_core.so
+LoadModule authz_host_module modules/mod_authz_host.so
+LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
+LoadModule authz_user_module modules/mod_authz_user.so
+#LoadModule authz_dbm_module modules/mod_authz_dbm.so
+#LoadModule authz_owner_module modules/mod_authz_owner.so
+#LoadModule authz_dbd_module modules/mod_authz_dbd.so
+LoadModule authz_core_module modules/mod_authz_core.so
+LoadModule access_compat_module modules/mod_access_compat.so
+LoadModule auth_basic_module modules/mod_auth_basic.so
+#LoadModule auth_form_module modules/mod_auth_form.so
+#LoadModule auth_digest_module modules/mod_auth_digest.so
+#LoadModule allowmethods_module modules/mod_allowmethods.so
+#LoadModule file_cache_module modules/mod_file_cache.so
+#LoadModule cache_module modules/mod_cache.so
+#LoadModule cache_disk_module modules/mod_cache_disk.so
+#LoadModule cache_socache_module modules/mod_cache_socache.so
+LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
+#LoadModule socache_dbm_module modules/mod_socache_dbm.so
+#LoadModule socache_memcache_module modules/mod_socache_memcache.so
+#LoadModule macro_module modules/mod_macro.so
+#LoadModule dbd_module modules/mod_dbd.so
+#LoadModule dumpio_module modules/mod_dumpio.so
+#LoadModule buffer_module modules/mod_buffer.so
+#LoadModule ratelimit_module modules/mod_ratelimit.so
+LoadModule reqtimeout_module modules/mod_reqtimeout.so
+#LoadModule ext_filter_module modules/mod_ext_filter.so
+#LoadModule request_module modules/mod_request.so
+#LoadModule include_module modules/mod_include.so
+LoadModule filter_module modules/mod_filter.so
+#LoadModule substitute_module modules/mod_substitute.so
+#LoadModule sed_module modules/mod_sed.so
+#LoadModule deflate_module modules/mod_deflate.so
+LoadModule mime_module modules/mod_mime.so
+LoadModule log_config_module modules/mod_log_config.so
+#LoadModule log_debug_module modules/mod_log_debug.so
+LoadModule logio_module modules/mod_logio.so
+LoadModule env_module modules/mod_env.so
+#LoadModule expires_module modules/mod_expires.so
+LoadModule headers_module modules/mod_headers.so
+#LoadModule unique_id_module modules/mod_unique_id.so
+LoadModule setenvif_module modules/mod_setenvif.so
+LoadModule version_module modules/mod_version.so
+#LoadModule remoteip_module modules/mod_remoteip.so
+LoadModule proxy_module modules/mod_proxy.so
+#LoadModule proxy_connect_module modules/mod_proxy_connect.so
+#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
+LoadModule proxy_http_module modules/mod_proxy_http.so
+LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
+#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
+#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
+#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
+#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
+#LoadModule proxy_express_module modules/mod_proxy_express.so
+#LoadModule session_module modules/mod_session.so
+#LoadModule session_cookie_module modules/mod_session_cookie.so
+#LoadModule session_crypto_module modules/mod_session_crypto.so
+#LoadModule session_dbd_module modules/mod_session_dbd.so
+#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
+LoadModule ssl_module modules/mod_ssl.so
+#LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
+#LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
+#LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
+#LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
+LoadModule unixd_module modules/mod_unixd.so
+#LoadModule dav_module modules/mod_dav.so
+LoadModule status_module modules/mod_status.so
+LoadModule autoindex_module modules/mod_autoindex.so
+#LoadModule info_module modules/mod_info.so
+LoadModule cgid_module modules/mod_cgid.so
+#LoadModule dav_fs_module modules/mod_dav_fs.so
+#LoadModule vhost_alias_module modules/mod_vhost_alias.so
+#LoadModule negotiation_module modules/mod_negotiation.so
+LoadModule dir_module modules/mod_dir.so
+#LoadModule actions_module modules/mod_actions.so
+#LoadModule speling_module modules/mod_speling.so
+#LoadModule userdir_module modules/mod_userdir.so
+LoadModule alias_module modules/mod_alias.so
+LoadModule rewrite_module modules/mod_rewrite.so
+
+#LoadModule fcgid_module modules/mod_fcgid.so
diff --git a/modules/http2/sandbox/test/conf/sites/aaa-noh2.example.org.conf b/modules/http2/sandbox/test/conf/sites/aaa-noh2.example.org.conf
new file mode 100644 (file)
index 0000000..81f092e
--- /dev/null
@@ -0,0 +1,48 @@
+################################################################################
+#
+# noh2.example.org
+#
+# a host reachable in clear/https that allow http/1.1 and *not* h2/h2c
+#
+################################################################################
+<VirtualHost *:SUBST_PORT_HTTPS_SUBST>
+    ServerName noh2.example.org
+    DocumentRoot "SUBST_SERVER_ROOT_SUBST/htdocs/test.example.org"
+
+    SSLEngine on
+       SSLCertificateFile SUBST_SERVER_ROOT_SUBST/conf/ssl/noh2.example.org.pem
+       SSLCertificateKeyFile SUBST_SERVER_ROOT_SUBST/conf/ssl/noh2.example.org.key
+
+    RewriteEngine on
+    RewriteRule ^/latest.tar.gz$ /xxx-1.0.2a.tar.gz [R=302,NC]
+
+    <IfModule h2_module>
+        H2Engine off
+    </IfModule>
+
+    <Location /hello.py>
+        SSLOptions +StdEnvVars
+    </Location>
+
+    <Location /greenbytes/>
+        ProxyPass http://www.greenbytes.de/
+        ProxyPassReverse http://www.greenbytes.de/
+        Order allow,deny
+        Allow from all
+    </Location>
+    ProxyPass /zeit http://www.zeit.de:80 max=20 ttl=120 retry=300
+</VirtualHost>
+
+
+<VirtualHost *:SUBST_PORT_HTTP_SUBST>
+       ServerName noh2.example.org:SUBST_PORT_HTTP_SUBST
+    DocumentRoot "SUBST_SERVER_ROOT_SUBST/htdocs/test.example.org"
+
+    RewriteEngine on
+    RewriteRule ^/latest.tar.gz$ /xxx-1.0.2a.tar.gz [R=302,NC]
+
+    <IfModule h2_module>
+        H2Engine off
+    </IfModule>
+
+</VirtualHost>
diff --git a/modules/http2/sandbox/test/conf/sites/test-ser.example.org.conf b/modules/http2/sandbox/test/conf/sites/test-ser.example.org.conf
new file mode 100644 (file)
index 0000000..867ff95
--- /dev/null
@@ -0,0 +1,81 @@
+################################################################################
+#
+# test-ser.example.org
+#
+# a host reachable in clear/https that allow http/1.1 and h2/h2c
+# with H2Serialize On
+#
+################################################################################
+
+<VirtualHost *:SUBST_PORT_HTTPS_SUBST>
+       ServerName test-ser.example.org
+    DocumentRoot "SUBST_SERVER_ROOT_SUBST/htdocs/test.example.org"
+
+    SSLEngine on
+    SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2
+    SSLCipherSuite HIGH:!aNULL:!MD5
+    SSLHonorCipherOrder on
+    SSLCertificateFile SUBST_SERVER_ROOT_SUBST/conf/ssl/test-ser.example.org.pem
+    SSLCertificateKeyFile SUBST_SERVER_ROOT_SUBST/conf/ssl/test-ser.example.org.key
+
+    RewriteEngine on
+    RewriteRule ^/latest.tar.gz$ /xxx-1.0.2a.tar.gz [R=302,NC]
+
+    <IfModule h2_module>
+        H2Engine on
+        H2SerializeHeaders on
+        H2AltSvc h2=test-ser.example.org:SUBST_PORT_HTTPS_SUBST
+        H2AltSvc h2c=:12345
+        H2AltSvc h2=mod-h2.greenbytes.de:SUBST_PORT_HTTPS_SUBST
+    </IfModule>
+
+    <Location /hello.py>
+        SSLOptions +StdEnvVars
+    </Location>
+
+    SSLProxyEngine on
+    SSLProxyProtocol +TLSv1 +TLSv1.1 +TLSv1.2
+    SSLProxyCipherSuite HIGH:!aNULL:!MD5
+    <Location /proxy/>
+        ProxyPass https://test-ser.example.org:SUBST_PORT_HTTPS_SUBST/
+        ProxyPassReverse https://test-ser.example.org:SUBST_PORT_HTTPS_SUBST/
+        Order allow,deny
+        Allow from all
+    </Location>
+    RewriteRule /rewrite(.*) https://test-ser.example.org:SUBST_PORT_HTTPS_SUBST$1 [P]
+
+    <Location /greenbytes/>
+        ProxyPass http://www.greenbytes.de/
+        ProxyPassReverse http://www.greenbytes.de/
+        Order allow,deny
+        Allow from all
+    </Location>
+    ProxyPass /zeit http://www.zeit.de:80 max=20 ttl=120 retry=300
+
+    <Location "/server-status">
+        SetHandler server-status
+    </Location>
+</VirtualHost>
+
+
+<VirtualHost *:SUBST_PORT_HTTP_SUBST>
+       ServerName test-ser.example.org:SUBST_PORT_HTTP_SUBST
+    DocumentRoot "SUBST_SERVER_ROOT_SUBST/htdocs/test.example.org"
+
+    RewriteEngine on
+    RewriteRule ^/latest.tar.gz$ /xxx-1.0.2a.tar.gz [R=302,NC]
+
+    <IfModule h2_module>
+        H2Engine on
+        H2SerializeHeaders on
+    </IfModule>
+
+    <Location /proxy/>
+        ProxyPass http://test-ser.example.org:SUBST_PORT_HTTP_SUBST/
+        ProxyPassReverse http://test-ser.example.org:SUBST_PORT_HTTP_SUBST/
+        Order allow,deny
+        Allow from all
+    </Location>
+    RewriteRule /rewrite(.*) http://test-ser.example.org:SUBST_PORT_HTTP_SUBST$1 [P]
+
+</VirtualHost>
diff --git a/modules/http2/sandbox/test/conf/sites/test.example.org.conf b/modules/http2/sandbox/test/conf/sites/test.example.org.conf
new file mode 100644 (file)
index 0000000..bbf0444
--- /dev/null
@@ -0,0 +1,91 @@
+################################################################################
+#
+# test.example.org
+#
+# a host reachable in clear/https that allow http/1.1 and h2/h2c
+#
+################################################################################
+<Directory "SUBST_SERVER_ROOT_SUBST/htdocs/test.example.org">
+    Options Indexes FollowSymLinks
+    AllowOverride None
+    Require all granted
+
+    AddHandler cgi-script .py
+    Options +ExecCGI
+
+    <IfModule fcgi_module>
+    FcgidWrapper SUBST_SERVER_ROOT_SUBST/bin/php-wrapper .php
+    </IfModule>
+</Directory>
+
+
+<VirtualHost *:SUBST_PORT_HTTPS_SUBST>
+       ServerName test.example.org
+    DocumentRoot "SUBST_SERVER_ROOT_SUBST/htdocs/test.example.org"
+
+    SSLEngine on
+    SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2
+    SSLCipherSuite HIGH:!aNULL:!MD5
+    SSLHonorCipherOrder on
+    SSLCertificateFile SUBST_SERVER_ROOT_SUBST/conf/ssl/test.example.org.pem
+    SSLCertificateKeyFile SUBST_SERVER_ROOT_SUBST/conf/ssl/test.example.org.key
+
+    RewriteEngine on
+    RewriteRule ^/latest.tar.gz$ /xxx-1.0.2a.tar.gz [R=302,NC]
+
+    <IfModule h2_module>
+        H2Engine on
+        H2AltSvc h2=test.example.org:12346
+        H2AltSvc h2c=:12345
+        H2AltSvc h2=mod-h2.greenbytes.de:12346
+    </IfModule>
+
+    <Location /hello.py>
+        SSLOptions +StdEnvVars
+    </Location>
+
+    SSLProxyEngine on
+    SSLProxyProtocol +TLSv1 +TLSv1.1 +TLSv1.2
+    SSLProxyCipherSuite HIGH:!aNULL:!MD5
+    <Location /proxy/>
+        ProxyPass https://test.example.org:SUBST_PORT_HTTPS_SUBST/
+        ProxyPassReverse https://test.example.org:SUBST_PORT_HTTPS_SUBST/
+        Order allow,deny
+        Allow from all
+    </Location>
+    RewriteRule /rewrite(.*) https://test.example.org:SUBST_PORT_HTTPS_SUBST$1 [P]
+
+    <Location /greenbytes/>
+        ProxyPass http://www.greenbytes.de/
+        ProxyPassReverse http://www.greenbytes.de/
+        Order allow,deny
+        Allow from all
+    </Location>
+    ProxyPass /zeit http://www.zeit.de:80 max=20 ttl=120 retry=300
+
+    <Location "/server-status">
+        SetHandler server-status
+    </Location>
+</VirtualHost>
+
+
+<VirtualHost *:SUBST_PORT_HTTP_SUBST>
+       ServerName test.example.org:SUBST_PORT_HTTP_SUBST
+    DocumentRoot "SUBST_SERVER_ROOT_SUBST/htdocs/test.example.org"
+
+    RewriteEngine on
+    RewriteRule ^/latest.tar.gz$ /xxx-1.0.2a.tar.gz [R=302,NC]
+
+    <IfModule h2_module>
+        H2Engine on
+    </IfModule>
+
+    <Location /proxy/>
+        ProxyPass http://test.example.org:SUBST_PORT_HTTP_SUBST/
+        ProxyPassReverse http://test.example.org:SUBST_PORT_HTTP_SUBST/
+        Order allow,deny
+        Allow from all
+    </Location>
+    RewriteRule /rewrite(.*) http://test.example.org:SUBST_PORT_HTTP_SUBST$1 [P]
+
+</VirtualHost>
diff --git a/modules/http2/sandbox/test/conf/ssl/.gitignore b/modules/http2/sandbox/test/conf/ssl/.gitignore
new file mode 100644 (file)
index 0000000..c996e50
--- /dev/null
@@ -0,0 +1 @@
+*.key
diff --git a/modules/http2/sandbox/test/conf/ssl/ca.pem b/modules/http2/sandbox/test/conf/ssl/ca.pem
new file mode 100644 (file)
index 0000000..960f265
--- /dev/null
@@ -0,0 +1,44 @@
+-----BEGIN CERTIFICATE-----
+MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
+pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
+OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
+Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
+Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
+HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
+Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
+Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
+26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
+AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
+ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
+LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
+BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
+Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
+dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
+cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
+YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
+dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
+bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
+YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
+TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
+9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
+jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
+FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
+ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
+ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
+EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
+L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
+yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
+O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
+um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
+NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
+-----END CERTIFICATE-----
diff --git a/modules/http2/sandbox/test/conf/ssl/cacerts.pem b/modules/http2/sandbox/test/conf/ssl/cacerts.pem
new file mode 100644 (file)
index 0000000..4a894c7
--- /dev/null
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIGNDCCBBygAwIBAgIBGDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NDE3WhcNMTcxMDI0MjA1NDE3WjCB
+jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
+IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
+YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj0PREGBiE
+gFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo/OenJOJA
+pgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn66+6CPAVv
+kvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+vWjhwRRI/
+ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxDKslIDlc5
+xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21TLwb0pwID
+AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
+L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
+YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
+dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
+c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
+BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
+BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
+LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAIQlJPqWIbuALi0jaMU2P91ZXouHTYlfp
+tVbzhUV1O+VQHwSL5qBaPucAroXQ+/8gA2TLrQLhxpFy+KNN1t7ozD+hiqLjfDen
+xk+PNdb01m4Ge90h2c9W/8swIkn+iQTzheWq8ecf6HWQTd35RvdCNPdFWAwRDYSw
+xtpdPvkBnufh2lWVvnQce/xNFE+sflVHfXv0pQ1JHpXo9xLBzP92piVH0PN1Nb6X
+t1gW66pceG/sUzCv6gRNzKkC4/C2BBL2MLERPZBOVmTX3DxDX3M570uvh+v2/miI
+RHLq0gfGabDBoYvvF0nXYbFFSF87ICHpW7LM9NfpMfULFWE7epTj69m8f5SuauNi
+YpaoZHy4h/OZMn6SolK+u/hlz8nyMPyLwcKmltdfieFcNID1j0cHL7SRv7Gifl9L
+WtBbnySGBVFaaQNlQ0lxxeBvlDRr9hvYqbBMflPrj0jfyjO1SPo2ShpTpjMM0InN
+SRXNiTE8kMBy12VLUjWKRhFEuT2OKGWmPnmeXAhEKa2wNREuIU640ucQPl2Eg7PD
+wuTSxv0JS3QJ3fGz0xk+gA2iCxnwOOfFwq/iI9th4p1cbiCJSS4jarJiwUW0n6+L
+p/EiO/h94pDQehn7Skzj0n1fSoMD7SfWI55rjbRZotnvbIIp3XUZPD9MEI3vu3Un
+0q6Dp6jOW6c=
+-----END CERTIFICATE-----
diff --git a/modules/http2/sandbox/test/conf/ssl/extensions.conf b/modules/http2/sandbox/test/conf/ssl/extensions.conf
new file mode 100644 (file)
index 0000000..4995e09
--- /dev/null
@@ -0,0 +1,5 @@
+[ ssl_client ]
+basicConstraints = CA:FALSE
+nsCertType = client
+keyUsage = digitalSignature, keyEncipherment
+extendedKeyUsage = clientAuth
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/conf/ssl/mod-h2.greenbytes.de.pem b/modules/http2/sandbox/test/conf/ssl/mod-h2.greenbytes.de.pem
new file mode 100644 (file)
index 0000000..036f6ce
--- /dev/null
@@ -0,0 +1,77 @@
+-----BEGIN CERTIFICATE-----
+MIIHTTCCBjWgAwIBAgIHBSitLk6z1TANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE
+BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE
+aWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0YXJ0Q29tIENs
+YXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMB4XDTE1MDIwMzIx
+MTMyOVoXDTE2MDIwNTA2MTUwM1owVTELMAkGA1UEBhMCREUxHTAbBgNVBAMTFG1v
+ZC1oMi5ncmVlbmJ5dGVzLmRlMScwJQYJKoZIhvcNAQkBFhhwb3N0bWFzdGVyQGdy
+ZWVuYnl0ZXMuZGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDqHjd2
+TvnN6nNBN/NegHLDN92gXhoakmommEASCMJQpWNGBWAkL2il4EiuJxMaGjEfIxUJ
+t94Wc7CH7DZvEkTzsjElopdmOTRaCnL+OIZG6LGTS9vLnnG0t4/4AahUqi9CRetM
+T6J+PodnFsJ1BhpnBPmGxei794MAQ4QuY/yQfd5Msh3/vs9gYix7dm6TRYY5+L6y
+/Ts6WmaBpnx1wMKRYyTPPmL1lHAjBGLaQtSD5TFgv9cno/YHngVfZrZRu+u2u+I2
+K83Qw60U16FWXChCCNAAEoHyXNN9Ulu08Nz0HIMowIajN36tzMq+CbLRbO+9RqP5
+RmxcPD0Ckpd0RkwflMaFIVOaTdXFnPQ+GFO1iRYgFdUQfIgDifnLaz5Q3Rqot5DQ
+pYZdlhOSI9uCPbyoJLXFERjqu15HWeyF1bQhmKWISEHBxl/rP2k1E+B3u4+V6Qli
+uaverlwqFIc0CEqbp3El68ZUoLwHYWmbhlW2VNQdzQ3p2yqABulcZuBDn0lpEy/q
+kJUao/gfwiZ+S/RiwQk866MvPHGa2INo5fQjm0IMXqWZpr+DHx4OT1I2+Y0y5Up0
+zRheHuxvNYDLJVb2891MGhgEbrADNApYOojdLsqAzYZaq+K1Xl2DzYKTV0uoT4x9
+6DMIKxEnJaSsVLUmSRZdxwwW2JAKhm/5C8BUfwIDAQABo4IC6DCCAuQwCQYDVR0T
+BAIwADALBgNVHQ8EBAMCA6gwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYE
+FFbumVYSCnUb68JUNXqgNT0Y+ymUMB8GA1UdIwQYMBaAFOtCNNCYsKuf9BtrCPfM
+ZC7vDixFMC4GA1UdEQQnMCWCFG1vZC1oMi5ncmVlbmJ5dGVzLmRlgg1ncmVlbmJ5
+dGVzLmRlMIIBVgYDVR0gBIIBTTCCAUkwCAYGZ4EMAQIBMIIBOwYLKwYBBAGBtTcB
+AgMwggEqMC4GCCsGAQUFBwIBFiJodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xp
+Y3kucGRmMIH3BggrBgEFBQcCAjCB6jAnFiBTdGFydENvbSBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTADAgEBGoG+VGhpcyBjZXJ0aWZpY2F0ZSB3YXMgaXNzdWVkIGFj
+Y29yZGluZyB0byB0aGUgQ2xhc3MgMSBWYWxpZGF0aW9uIHJlcXVpcmVtZW50cyBv
+ZiB0aGUgU3RhcnRDb20gQ0EgcG9saWN5LCByZWxpYW5jZSBvbmx5IGZvciB0aGUg
+aW50ZW5kZWQgcHVycG9zZSBpbiBjb21wbGlhbmNlIG9mIHRoZSByZWx5aW5nIHBh
+cnR5IG9ibGlnYXRpb25zLjA1BgNVHR8ELjAsMCqgKKAmhiRodHRwOi8vY3JsLnN0
+YXJ0c3NsLmNvbS9jcnQxLWNybC5jcmwwgY4GCCsGAQUFBwEBBIGBMH8wOQYIKwYB
+BQUHMAGGLWh0dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbS9zdWIvY2xhc3MxL3NlcnZl
+ci9jYTBCBggrBgEFBQcwAoY2aHR0cDovL2FpYS5zdGFydHNzbC5jb20vY2VydHMv
+c3ViLmNsYXNzMS5zZXJ2ZXIuY2EuY3J0MCMGA1UdEgQcMBqGGGh0dHA6Ly93d3cu
+c3RhcnRzc2wuY29tLzANBgkqhkiG9w0BAQsFAAOCAQEAhoTjVHYMOHJGkRMV5NPX
+pm4/gaGT29UXN/XGqk8Qfvi8w7C38Ypc8UFisOhl7eHMkIKy4rua/t5aYve/RgFA
+zEwAV79UKDwn6KkaHn2EFbNt8LXLpuUujc43gVX0Me+CAL/NQV9OS97ltS6BBBgj
+UdEmqBzbvYyAus1xDLbZnzhDG1CMBgcxAhEZn40v3ii0iMuQ+sMCO2oy3e23wQ7I
+FKhEI28r1hk9s0ASRHLMGOW15wccm2SI/cMVPuNapoj1hSEo2Gpcmb3KQtrsG6PA
+kq8x/8W1pDVuaQV992nkYgCzjMhVP6ZahtET0r6Kth5JaJgThuRacXLvyg8ltKmF
+Rg==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIF2TCCA8GgAwIBAgIHFxU9nqs/vzANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQG
+EwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERp
+Z2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDcxMDE0MjA1NDE3WhcNMjIxMDE0MjA1
+NDE3WjCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzAp
+BgNVBAsTIlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNV
+BAMTL1N0YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVy
+IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj
+0PREGBiEgFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo
+/OenJOJApgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn6
+6+6CPAVvkvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+v
+WjhwRRI/ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxD
+KslIDlc5xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21T
+Lwb0pwIDAQABo4IBTDCCAUgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
+BAMCAQYwHQYDVR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaA
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGkGCCsGAQUFBwEBBF0wWzAnBggrBgEFBQcw
+AYYbaHR0cDovL29jc3Auc3RhcnRzc2wuY29tL2NhMDAGCCsGAQUFBzAChiRodHRw
+Oi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9jYS5jcnQwMgYDVR0fBCswKTAnoCWg
+I4YhaHR0cDovL2NybC5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMEMGA1UdIAQ8MDow
+OAYEVR0gADAwMC4GCCsGAQUFBwIBFiJodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9w
+b2xpY3kucGRmMA0GCSqGSIb3DQEBCwUAA4ICAQCBnsOw7dxamNbdJb/ydkh4Qb6E
+qgEU+G9hCCIGXwhWRZMYczNJMrpVvyLq5mNOmrFPC7bJrqYV+vEOYHNXrzthLyOG
+FFOVQe2cxbmQecFOvbkWVlYAIaTG42sHKVi+RFsG2jRNZcFhHnsFnLPMyE6148lZ
+wVdZGsxZvpeHReNUpW0jh7uq90sShFzHs4f7wJ5XmiHOL7fZbnFV6uE/OoFnBWif
+CRnd9+RE3uCospESPCRPdbG+Q4GQ+MBS1moXDTRB6DcNoHvqC6eU3r8/Fn/DeA9w
+9JHPXUfrAhZYKyOQUIqcfE5bvssaY+oODVxji6BMk8VSVHsJ4FSC1/7Pkt/UPoQp
+FVh38wIJnvEUeNVmVl3HHFYTd50irdKYPBC63qi2V/YYI6bJKmbrjfP9Vhyt9uNr
+y3Kh4W22ktDuCCvWC7n/gqerdq+VlTRfNt7D/mB0irnaKjEVNCXBXm9V/978J+Ez
+8aplGZccQ9jnc9kiPtUp5dj45E3V8vKqzp9srSSI5Xapdg+ZcPY+6HNuVB+MadRp
+ZW2One/Qnzg9B4GnVX7MOETImdoP4kXpostFuxoY/5LxCU1LJAIENV4txvT50lX2
+GBXCkxllRLWOgdyll11ift/4IO1aCOGDijGIfh498YisM1LGxytmGcxvbJERVri+
+gGpWAZ5J6dvtf0s+bA==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/conf/ssl/noh2.example.org.x509.input b/modules/http2/sandbox/test/conf/ssl/noh2.example.org.x509.input
new file mode 100644 (file)
index 0000000..450cc11
--- /dev/null
@@ -0,0 +1,9 @@
+DE
+NRW
+Muenster
+greenbytes GmbH
+.
+noh2.example.org
+.
+.
+.
diff --git a/modules/http2/sandbox/test/conf/ssl/test-ser.example.org.x509.input b/modules/http2/sandbox/test/conf/ssl/test-ser.example.org.x509.input
new file mode 100644 (file)
index 0000000..55c5e99
--- /dev/null
@@ -0,0 +1,9 @@
+DE
+NRW
+Muenster
+greenbytes GmbH
+.
+test-ser.example.org
+.
+.
+.
diff --git a/modules/http2/sandbox/test/conf/ssl/test.example.org.x509.input b/modules/http2/sandbox/test/conf/ssl/test.example.org.x509.input
new file mode 100644 (file)
index 0000000..aeb6c95
--- /dev/null
@@ -0,0 +1,9 @@
+DE
+NRW
+Muenster
+greenbytes GmbH
+.
+test.example.org
+.
+.
+.
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/001.html b/modules/http2/sandbox/test/htdocs/test.example.org/001.html
new file mode 100755 (executable)
index 0000000..184952d
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML> \r
+ <html>\r
+   <head>\r
+     <title>HTML/2.0 Test File: 001</title>\r
+   </head>\r
+   <body>\r
+     <p><h1>HTML/2.0 Test File: 001</h1></p>\r
+     <p>This file only contains a simple HTML structure with plain text.</p>\r
+   </body>\r
+</html>\r
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/002.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/002.jpg
new file mode 100755 (executable)
index 0000000..3feefb0
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/002.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/003.html b/modules/http2/sandbox/test/htdocs/test.example.org/003.html
new file mode 100755 (executable)
index 0000000..d5b08c5
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML> \r
+ <html>\r
+   <head>\r
+     <title>HTML/2.0 Test File: 003</title>\r
+   </head>\r
+   <body>\r
+     <p><h1>HTML/2.0 Test File: 003</h1></p>\r
+     <p>This is a text HTML file with a big image:</p>\r
+        <p><img src="003/003_img.jpg" alt="GSMA Logo" style="width:269px;height:249px"></p>\r
+   </body>\r
+</html>\r
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/003/003_img.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/003/003_img.jpg
new file mode 100755 (executable)
index 0000000..3feefb0
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/003/003_img.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004.html b/modules/http2/sandbox/test/htdocs/test.example.org/004.html
new file mode 100755 (executable)
index 0000000..768cb82
--- /dev/null
@@ -0,0 +1,23 @@
+<html>\r
+       <head>\r
+               <title>HTML/2.0 Test File: 004</title>\r
+       </head>\r
+       <body>\r
+               <p><h1>HTML/2.0 Test File: 004</h1>\r
+               This file contains plain text with a bunch of images.<br>\r
+               <img src="004/gophertiles_142.jpg" height="32" width="32"><img src="004/gophertiles_084.jpg" height="32" width="32"><img src="004/gophertiles_052.jpg" height="32" width="32"><img src="004/gophertiles_077.jpg" height="32" width="32"><img src="004/gophertiles_030.jpg" height="32" width="32"><img src="004/gophertiles_027.jpg" height="32" width="32"><img src="004/gophertiles_039.jpg" height="32" width="32"><img src="004/gophertiles_025.jpg" height="32" width="32"><img src="004/gophertiles_017.jpg" height="32" width="32"><img src="004/gophertiles_179.jpg" height="32" width="32"><img src="004/gophertiles_032.jpg" height="32" width="32"><img src="004/gophertiles_161.jpg" height="32" width="32"><img src="004/gophertiles_088.jpg" height="32" width="32"><img src="004/gophertiles_022.jpg" height="32" width="32"><img src="004/gophertiles_146.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_102.jpg" height="32" width="32"><img src="004/gophertiles_009.jpg" height="32" width="32"><img src="004/gophertiles_132.jpg" height="32" width="32"><img src="004/gophertiles_137.jpg" height="32" width="32"><img src="004/gophertiles_055.jpg" height="32" width="32"><img src="004/gophertiles_036.jpg" height="32" width="32"><img src="004/gophertiles_127.jpg" height="32" width="32"><img src="004/gophertiles_145.jpg" height="32" width="32"><img src="004/gophertiles_147.jpg" height="32" width="32"><img src="004/gophertiles_153.jpg" height="32" width="32"><img src="004/gophertiles_105.jpg" height="32" width="32"><img src="004/gophertiles_103.jpg" height="32" width="32"><img src="004/gophertiles_033.jpg" height="32" width="32"><img src="004/gophertiles_054.jpg" height="32" width="32"><img src="004/gophertiles_015.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_016.jpg" height="32" width="32"><img src="004/gophertiles_072.jpg" height="32" width="32"><img src="004/gophertiles_115.jpg" height="32" width="32"><img src="004/gophertiles_108.jpg" height="32" width="32"><img src="004/gophertiles_148.jpg" height="32" width="32"><img src="004/gophertiles_070.jpg" height="32" width="32"><img src="004/gophertiles_083.jpg" height="32" width="32"><img src="004/gophertiles_118.jpg" height="32" width="32"><img src="004/gophertiles_053.jpg" height="32" width="32"><img src="004/gophertiles_021.jpg" height="32" width="32"><img src="004/gophertiles_059.jpg" height="32" width="32"><img src="004/gophertiles_130.jpg" height="32" width="32"><img src="004/gophertiles_163.jpg" height="32" width="32"><img src="004/gophertiles_098.jpg" height="32" width="32"><img src="004/gophertiles_064.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_018.jpg" height="32" width="32"><img src="004/gophertiles_058.jpg" height="32" width="32"><img src="004/gophertiles_167.jpg" height="32" width="32"><img src="004/gophertiles_082.jpg" height="32" width="32"><img src="004/gophertiles_056.jpg" height="32" width="32"><img src="004/gophertiles_180.jpg" height="32" width="32"><img src="004/gophertiles_046.jpg" height="32" width="32"><img src="004/gophertiles_093.jpg" height="32" width="32"><img src="004/gophertiles_106.jpg" height="32" width="32"><img src="004/gophertiles_065.jpg" height="32" width="32"><img src="004/gophertiles_175.jpg" height="32" width="32"><img src="004/gophertiles_139.jpg" height="32" width="32"><img src="004/gophertiles_101.jpg" height="32" width="32"><img src="004/gophertiles_099.jpg" height="32" width="32"><img src="004/gophertiles_051.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_140.jpg" height="32" width="32"><img src="004/gophertiles_134.jpg" height="32" width="32"><img src="004/gophertiles_149.jpg" height="32" width="32"><img src="004/gophertiles_049.jpg" height="32" width="32"><img src="004/gophertiles_095.jpg" height="32" width="32"><img src="004/gophertiles_075.jpg" height="32" width="32"><img src="004/gophertiles_066.jpg" height="32" width="32"><img src="004/gophertiles_090.jpg" height="32" width="32"><img src="004/gophertiles_035.jpg" height="32" width="32"><img src="004/gophertiles_114.jpg" height="32" width="32"><img src="004/gophertiles_160.jpg" height="32" width="32"><img src="004/gophertiles_079.jpg" height="32" width="32"><img src="004/gophertiles_062.jpg" height="32" width="32"><img src="004/gophertiles_096.jpg" height="32" width="32"><img src="004/gophertiles_100.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_104.jpg" height="32" width="32"><img src="004/gophertiles_057.jpg" height="32" width="32"><img src="004/gophertiles_037.jpg" height="32" width="32"><img src="004/gophertiles_086.jpg" height="32" width="32"><img src="004/gophertiles_168.jpg" height="32" width="32"><img src="004/gophertiles_138.jpg" height="32" width="32"><img src="004/gophertiles_045.jpg" height="32" width="32"><img src="004/gophertiles_141.jpg" height="32" width="32"><img src="004/gophertiles_029.jpg" height="32" width="32"><img src="004/gophertiles_165.jpg" height="32" width="32"><img src="004/gophertiles_110.jpg" height="32" width="32"><img src="004/gophertiles_063.jpg" height="32" width="32"><img src="004/gophertiles_158.jpg" height="32" width="32"><img src="004/gophertiles_122.jpg" height="32" width="32"><img src="004/gophertiles_068.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_170.jpg" height="32" width="32"><img src="004/gophertiles_120.jpg" height="32" width="32"><img src="004/gophertiles_117.jpg" height="32" width="32"><img src="004/gophertiles_031.jpg" height="32" width="32"><img src="004/gophertiles_113.jpg" height="32" width="32"><img src="004/gophertiles_074.jpg" height="32" width="32"><img src="004/gophertiles_129.jpg" height="32" width="32"><img src="004/gophertiles_019.jpg" height="32" width="32"><img src="004/gophertiles_060.jpg" height="32" width="32"><img src="004/gophertiles_109.jpg" height="32" width="32"><img src="004/gophertiles_080.jpg" height="32" width="32"><img src="004/gophertiles_097.jpg" height="32" width="32"><img src="004/gophertiles_116.jpg" height="32" width="32"><img src="004/gophertiles_085.jpg" height="32" width="32"><img src="004/gophertiles_050.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_151.jpg" height="32" width="32"><img src="004/gophertiles_094.jpg" height="32" width="32"><img src="004/gophertiles_067.jpg" height="32" width="32"><img src="004/gophertiles_128.jpg" height="32" width="32"><img src="004/gophertiles_034.jpg" height="32" width="32"><img src="004/gophertiles_135.jpg" height="32" width="32"><img src="004/gophertiles_012.jpg" height="32" width="32"><img src="004/gophertiles_010.jpg" height="32" width="32"><img src="004/gophertiles_152.jpg" height="32" width="32"><img src="004/gophertiles_171.jpg" height="32" width="32"><img src="004/gophertiles_087.jpg" height="32" width="32"><img src="004/gophertiles_126.jpg" height="32" width="32"><img src="004/gophertiles_048.jpg" height="32" width="32"><img src="004/gophertiles_023.jpg" height="32" width="32"><img src="004/gophertiles_078.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_071.jpg" height="32" width="32"><img src="004/gophertiles_131.jpg" height="32" width="32"><img src="004/gophertiles_073.jpg" height="32" width="32"><img src="004/gophertiles_143.jpg" height="32" width="32"><img src="004/gophertiles_173.jpg" height="32" width="32"><img src="004/gophertiles_154.jpg" height="32" width="32"><img src="004/gophertiles_061.jpg" height="32" width="32"><img src="004/gophertiles_178.jpg" height="32" width="32"><img src="004/gophertiles_013.jpg" height="32" width="32"><img src="004/gophertiles_028.jpg" height="32" width="32"><img src="004/gophertiles_157.jpg" height="32" width="32"><img src="004/gophertiles_038.jpg" height="32" width="32"><img src="004/gophertiles_069.jpg" height="32" width="32"><img src="004/gophertiles_174.jpg" height="32" width="32"><img src="004/gophertiles_076.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_155.jpg" height="32" width="32"><img src="004/gophertiles_107.jpg" height="32" width="32"><img src="004/gophertiles_136.jpg" height="32" width="32"><img src="004/gophertiles_144.jpg" height="32" width="32"><img src="004/gophertiles_091.jpg" height="32" width="32"><img src="004/gophertiles_024.jpg" height="32" width="32"><img src="004/gophertiles_014.jpg" height="32" width="32"><img src="004/gophertiles_159.jpg" height="32" width="32"><img src="004/gophertiles_011.jpg" height="32" width="32"><img src="004/gophertiles_176.jpg" height="32" width="32"><img src="004/gophertiles_162.jpg" height="32" width="32"><img src="004/gophertiles_156.jpg" height="32" width="32"><img src="004/gophertiles_081.jpg" height="32" width="32"><img src="004/gophertiles_119.jpg" height="32" width="32"><img src="004/gophertiles_026.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_133.jpg" height="32" width="32"><img src="004/gophertiles_020.jpg" height="32" width="32"><img src="004/gophertiles_044.jpg" height="32" width="32"><img src="004/gophertiles_125.jpg" height="32" width="32"><img src="004/gophertiles_150.jpg" height="32" width="32"><img src="004/gophertiles_172.jpg" height="32" width="32"><img src="004/gophertiles_002.jpg" height="32" width="32"><img src="004/gophertiles_169.jpg" height="32" width="32"><img src="004/gophertiles_007.jpg" height="32" width="32"><img src="004/gophertiles_008.jpg" height="32" width="32"><img src="004/gophertiles_042.jpg" height="32" width="32"><img src="004/gophertiles_041.jpg" height="32" width="32"><img src="004/gophertiles_166.jpg" height="32" width="32"><img src="004/gophertiles_005.jpg" height="32" width="32"><img src="004/gophertiles_089.jpg" height="32" width="32"><br>\r
+               <img src="004/gophertiles_177.jpg" height="32" width="32"><img src="004/gophertiles_092.jpg" height="32" width="32"><img src="004/gophertiles_043.jpg" height="32" width="32"><img src="004/gophertiles_111.jpg" height="32" width="32"><img src="004/gophertiles_047.jpg" height="32" width="32"><img src="004/gophertiles.jpg" height="32" width="32"><img src="004/gophertiles_006.jpg" height="32" width="32"><img src="004/gophertiles_121.jpg" height="32" width="32"><img src="004/gophertiles_004.jpg" height="32" width="32"><img src="004/gophertiles_124.jpg" height="32" width="32"><img src="004/gophertiles_123.jpg" height="32" width="32"><img src="004/gophertiles_112.jpg" height="32" width="32"><img src="004/gophertiles_040.jpg" height="32" width="32"><img src="004/gophertiles_164.jpg" height="32" width="32"><img src="004/gophertiles_003.jpg" height="32" width="32"><br>\r
+               <hr>This page is developed using this template:<a href="https://http2.golang.org/">HTTP/2 demo server</a>\r
+               </p>\r
+       </body>\r
+</html>
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles.jpg
new file mode 100755 (executable)
index 0000000..e45ac3b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_002.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_002.jpg
new file mode 100755 (executable)
index 0000000..91121de
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_002.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_003.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_003.jpg
new file mode 100755 (executable)
index 0000000..a26648f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_003.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_004.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_004.jpg
new file mode 100755 (executable)
index 0000000..1d2db98
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_004.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_005.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_005.jpg
new file mode 100755 (executable)
index 0000000..05a298c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_005.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_006.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_006.jpg
new file mode 100755 (executable)
index 0000000..54a4920
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_006.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_007.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_007.jpg
new file mode 100755 (executable)
index 0000000..526f850
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_007.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_008.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_008.jpg
new file mode 100755 (executable)
index 0000000..35f5a2f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_008.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_009.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_009.jpg
new file mode 100755 (executable)
index 0000000..96ec2b8
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_009.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_010.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_010.jpg
new file mode 100755 (executable)
index 0000000..95a9509
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_010.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_011.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_011.jpg
new file mode 100755 (executable)
index 0000000..65701ed
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_011.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_012.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_012.jpg
new file mode 100755 (executable)
index 0000000..6242fa6
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_012.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_013.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_013.jpg
new file mode 100755 (executable)
index 0000000..8096ab2
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_013.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_014.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_014.jpg
new file mode 100755 (executable)
index 0000000..e027312
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_014.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_015.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_015.jpg
new file mode 100755 (executable)
index 0000000..a27076d
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_015.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_016.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_016.jpg
new file mode 100755 (executable)
index 0000000..04b20db
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_016.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_017.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_017.jpg
new file mode 100755 (executable)
index 0000000..9b6e44b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_017.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_018.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_018.jpg
new file mode 100755 (executable)
index 0000000..209b6fd
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_018.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_019.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_019.jpg
new file mode 100755 (executable)
index 0000000..3bc23a3
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_019.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_020.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_020.jpg
new file mode 100755 (executable)
index 0000000..ba04297
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_020.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_021.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_021.jpg
new file mode 100755 (executable)
index 0000000..f5a422f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_021.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_022.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_022.jpg
new file mode 100755 (executable)
index 0000000..cb49051
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_022.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_023.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_023.jpg
new file mode 100755 (executable)
index 0000000..7e83a7e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_023.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_024.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_024.jpg
new file mode 100755 (executable)
index 0000000..87c711b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_024.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_025.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_025.jpg
new file mode 100755 (executable)
index 0000000..c42eb3c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_025.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_026.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_026.jpg
new file mode 100755 (executable)
index 0000000..29f9da6
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_026.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_027.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_027.jpg
new file mode 100755 (executable)
index 0000000..6ceccde
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_027.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_028.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_028.jpg
new file mode 100755 (executable)
index 0000000..6e3cb34
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_028.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_029.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_029.jpg
new file mode 100755 (executable)
index 0000000..dac302b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_029.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_030.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_030.jpg
new file mode 100755 (executable)
index 0000000..4299071
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_030.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_031.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_031.jpg
new file mode 100755 (executable)
index 0000000..739924f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_031.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_032.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_032.jpg
new file mode 100755 (executable)
index 0000000..4685513
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_032.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_033.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_033.jpg
new file mode 100755 (executable)
index 0000000..26ea0e1
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_033.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_034.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_034.jpg
new file mode 100755 (executable)
index 0000000..f02930e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_034.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_035.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_035.jpg
new file mode 100755 (executable)
index 0000000..8b6bde8
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_035.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_036.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_036.jpg
new file mode 100755 (executable)
index 0000000..23ac1c0
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_036.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_037.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_037.jpg
new file mode 100755 (executable)
index 0000000..6de6681
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_037.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_038.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_038.jpg
new file mode 100755 (executable)
index 0000000..aea11a3
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_038.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_039.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_039.jpg
new file mode 100755 (executable)
index 0000000..bb54d13
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_039.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_040.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_040.jpg
new file mode 100755 (executable)
index 0000000..91591af
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_040.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_041.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_041.jpg
new file mode 100755 (executable)
index 0000000..96b13dd
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_041.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_042.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_042.jpg
new file mode 100755 (executable)
index 0000000..0ef80f1
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_042.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_043.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_043.jpg
new file mode 100755 (executable)
index 0000000..c3828a7
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_043.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_044.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_044.jpg
new file mode 100755 (executable)
index 0000000..036ec10
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_044.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_045.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_045.jpg
new file mode 100755 (executable)
index 0000000..03f5413
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_045.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_046.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_046.jpg
new file mode 100755 (executable)
index 0000000..8353e24
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_046.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_047.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_047.jpg
new file mode 100755 (executable)
index 0000000..86e4d88
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_047.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_048.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_048.jpg
new file mode 100755 (executable)
index 0000000..8f308ed
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_048.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_049.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_049.jpg
new file mode 100755 (executable)
index 0000000..bf22844
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_049.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_050.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_050.jpg
new file mode 100755 (executable)
index 0000000..65addde
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_050.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_051.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_051.jpg
new file mode 100755 (executable)
index 0000000..aabb52b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_051.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_052.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_052.jpg
new file mode 100755 (executable)
index 0000000..3d4bad8
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_052.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_053.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_053.jpg
new file mode 100755 (executable)
index 0000000..d30c4d0
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_053.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_054.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_054.jpg
new file mode 100755 (executable)
index 0000000..c27a34c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_054.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_055.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_055.jpg
new file mode 100755 (executable)
index 0000000..bac6e3f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_055.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_056.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_056.jpg
new file mode 100755 (executable)
index 0000000..246624e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_056.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_057.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_057.jpg
new file mode 100755 (executable)
index 0000000..0122037
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_057.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_058.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_058.jpg
new file mode 100755 (executable)
index 0000000..71f602f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_058.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_059.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_059.jpg
new file mode 100755 (executable)
index 0000000..78b0dd1
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_059.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_060.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_060.jpg
new file mode 100755 (executable)
index 0000000..b2c699c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_060.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_061.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_061.jpg
new file mode 100755 (executable)
index 0000000..082fe53
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_061.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_062.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_062.jpg
new file mode 100755 (executable)
index 0000000..9b3bd8a
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_062.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_063.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_063.jpg
new file mode 100755 (executable)
index 0000000..34bbfc5
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_063.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_064.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_064.jpg
new file mode 100755 (executable)
index 0000000..ac0ddc7
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_064.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_065.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_065.jpg
new file mode 100755 (executable)
index 0000000..f85dce5
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_065.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_066.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_066.jpg
new file mode 100755 (executable)
index 0000000..616dd5c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_066.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_067.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_067.jpg
new file mode 100755 (executable)
index 0000000..bbbaecf
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_067.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_068.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_068.jpg
new file mode 100755 (executable)
index 0000000..d0b6a18
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_068.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_069.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_069.jpg
new file mode 100755 (executable)
index 0000000..27e1abc
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_069.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_070.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_070.jpg
new file mode 100755 (executable)
index 0000000..de1a15e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_070.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_071.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_071.jpg
new file mode 100755 (executable)
index 0000000..40912e3
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_071.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_072.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_072.jpg
new file mode 100755 (executable)
index 0000000..ef01d06
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_072.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_073.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_073.jpg
new file mode 100755 (executable)
index 0000000..3298be7
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_073.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_074.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_074.jpg
new file mode 100755 (executable)
index 0000000..28fb75c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_074.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_075.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_075.jpg
new file mode 100755 (executable)
index 0000000..1f70c5e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_075.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_076.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_076.jpg
new file mode 100755 (executable)
index 0000000..d929f53
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_076.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_077.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_077.jpg
new file mode 100755 (executable)
index 0000000..49c8ca1
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_077.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_078.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_078.jpg
new file mode 100755 (executable)
index 0000000..a21dd87
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_078.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_079.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_079.jpg
new file mode 100755 (executable)
index 0000000..bfbd4c2
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_079.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_080.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_080.jpg
new file mode 100755 (executable)
index 0000000..6ff068c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_080.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_081.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_081.jpg
new file mode 100755 (executable)
index 0000000..dd615c7
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_081.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_082.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_082.jpg
new file mode 100755 (executable)
index 0000000..0c28382
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_082.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_083.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_083.jpg
new file mode 100755 (executable)
index 0000000..5512c16
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_083.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_084.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_084.jpg
new file mode 100755 (executable)
index 0000000..d08ac7b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_084.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_085.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_085.jpg
new file mode 100755 (executable)
index 0000000..c098f72
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_085.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_086.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_086.jpg
new file mode 100755 (executable)
index 0000000..203e41d
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_086.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_087.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_087.jpg
new file mode 100755 (executable)
index 0000000..b664135
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_087.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_088.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_088.jpg
new file mode 100755 (executable)
index 0000000..e211d21
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_088.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_089.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_089.jpg
new file mode 100755 (executable)
index 0000000..00c4730
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_089.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_090.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_090.jpg
new file mode 100755 (executable)
index 0000000..7203f10
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_090.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_091.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_091.jpg
new file mode 100755 (executable)
index 0000000..f57baa9
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_091.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_092.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_092.jpg
new file mode 100755 (executable)
index 0000000..cba16c6
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_092.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_093.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_093.jpg
new file mode 100755 (executable)
index 0000000..6d4c1a5
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_093.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_094.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_094.jpg
new file mode 100755 (executable)
index 0000000..a5f6a2a
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_094.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_095.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_095.jpg
new file mode 100755 (executable)
index 0000000..d213fe5
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_095.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_096.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_096.jpg
new file mode 100755 (executable)
index 0000000..0fd51eb
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_096.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_097.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_097.jpg
new file mode 100755 (executable)
index 0000000..2b706cc
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_097.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_098.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_098.jpg
new file mode 100755 (executable)
index 0000000..7861f2a
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_098.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_099.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_099.jpg
new file mode 100755 (executable)
index 0000000..be10042
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_099.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_100.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_100.jpg
new file mode 100755 (executable)
index 0000000..8687873
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_100.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_101.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_101.jpg
new file mode 100755 (executable)
index 0000000..fe4b56a
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_101.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_102.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_102.jpg
new file mode 100755 (executable)
index 0000000..d888f6c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_102.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_103.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_103.jpg
new file mode 100755 (executable)
index 0000000..4ebf13d
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_103.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_104.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_104.jpg
new file mode 100755 (executable)
index 0000000..b4dc051
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_104.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_105.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_105.jpg
new file mode 100755 (executable)
index 0000000..4f3c5a1
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_105.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_106.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_106.jpg
new file mode 100755 (executable)
index 0000000..51d6742
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_106.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_107.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_107.jpg
new file mode 100755 (executable)
index 0000000..ef986b7
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_107.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_108.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_108.jpg
new file mode 100755 (executable)
index 0000000..8901141
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_108.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_109.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_109.jpg
new file mode 100755 (executable)
index 0000000..a946a2b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_109.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_110.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_110.jpg
new file mode 100755 (executable)
index 0000000..35d542c
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_110.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_111.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_111.jpg
new file mode 100755 (executable)
index 0000000..0ec9641
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_111.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_112.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_112.jpg
new file mode 100755 (executable)
index 0000000..530739a
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_112.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_113.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_113.jpg
new file mode 100755 (executable)
index 0000000..0537d7f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_113.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_114.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_114.jpg
new file mode 100755 (executable)
index 0000000..9ecb936
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_114.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_115.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_115.jpg
new file mode 100755 (executable)
index 0000000..221e6f4
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_115.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_116.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_116.jpg
new file mode 100755 (executable)
index 0000000..0de1084
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_116.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_117.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_117.jpg
new file mode 100755 (executable)
index 0000000..8ebd1ea
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_117.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_118.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_118.jpg
new file mode 100755 (executable)
index 0000000..246d055
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_118.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_119.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_119.jpg
new file mode 100755 (executable)
index 0000000..8d92e15
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_119.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_120.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_120.jpg
new file mode 100755 (executable)
index 0000000..8ebef73
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_120.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_121.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_121.jpg
new file mode 100755 (executable)
index 0000000..e7a3772
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_121.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_122.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_122.jpg
new file mode 100755 (executable)
index 0000000..6a57fc8
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_122.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_123.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_123.jpg
new file mode 100755 (executable)
index 0000000..b941523
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_123.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_124.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_124.jpg
new file mode 100755 (executable)
index 0000000..9dddf38
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_124.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_125.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_125.jpg
new file mode 100755 (executable)
index 0000000..d2e479e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_125.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_126.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_126.jpg
new file mode 100755 (executable)
index 0000000..32fc518
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_126.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_127.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_127.jpg
new file mode 100755 (executable)
index 0000000..c5f71cc
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_127.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_128.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_128.jpg
new file mode 100755 (executable)
index 0000000..d899e3d
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_128.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_129.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_129.jpg
new file mode 100755 (executable)
index 0000000..3508872
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_129.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_130.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_130.jpg
new file mode 100755 (executable)
index 0000000..b26d716
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_130.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_131.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_131.jpg
new file mode 100755 (executable)
index 0000000..56a27d4
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_131.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_132.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_132.jpg
new file mode 100755 (executable)
index 0000000..b34a2f0
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_132.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_133.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_133.jpg
new file mode 100755 (executable)
index 0000000..b5dc4da
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_133.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_134.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_134.jpg
new file mode 100755 (executable)
index 0000000..24d6866
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_134.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_135.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_135.jpg
new file mode 100755 (executable)
index 0000000..f0c27c8
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_135.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_136.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_136.jpg
new file mode 100755 (executable)
index 0000000..d3b3b28
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_136.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_137.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_137.jpg
new file mode 100755 (executable)
index 0000000..7e78d35
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_137.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_138.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_138.jpg
new file mode 100755 (executable)
index 0000000..5a0024e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_138.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_139.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_139.jpg
new file mode 100755 (executable)
index 0000000..e0e16bc
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_139.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_140.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_140.jpg
new file mode 100755 (executable)
index 0000000..b9c54c4
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_140.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_141.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_141.jpg
new file mode 100755 (executable)
index 0000000..f62eada
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_141.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_142.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_142.jpg
new file mode 100755 (executable)
index 0000000..6085722
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_142.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_143.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_143.jpg
new file mode 100755 (executable)
index 0000000..f533fe5
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_143.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_144.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_144.jpg
new file mode 100755 (executable)
index 0000000..bcc5602
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_144.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_145.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_145.jpg
new file mode 100755 (executable)
index 0000000..3b9402e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_145.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_146.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_146.jpg
new file mode 100755 (executable)
index 0000000..f2f049b
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_146.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_147.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_147.jpg
new file mode 100755 (executable)
index 0000000..06fc738
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_147.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_148.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_148.jpg
new file mode 100755 (executable)
index 0000000..e094d96
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_148.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_149.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_149.jpg
new file mode 100755 (executable)
index 0000000..26ab8d7
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_149.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_150.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_150.jpg
new file mode 100755 (executable)
index 0000000..02ca417
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_150.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_151.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_151.jpg
new file mode 100755 (executable)
index 0000000..78fe841
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_151.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_152.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_152.jpg
new file mode 100755 (executable)
index 0000000..9cfa47a
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_152.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_153.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_153.jpg
new file mode 100755 (executable)
index 0000000..0a67731
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_153.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_154.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_154.jpg
new file mode 100755 (executable)
index 0000000..9a38955
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_154.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_155.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_155.jpg
new file mode 100755 (executable)
index 0000000..5a10b47
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_155.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_156.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_156.jpg
new file mode 100755 (executable)
index 0000000..809d5f9
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_156.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_157.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_157.jpg
new file mode 100755 (executable)
index 0000000..8c852e2
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_157.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_158.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_158.jpg
new file mode 100755 (executable)
index 0000000..5ef80f7
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_158.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_159.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_159.jpg
new file mode 100755 (executable)
index 0000000..2fe485f
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_159.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_160.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_160.jpg
new file mode 100755 (executable)
index 0000000..072cfc6
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_160.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_161.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_161.jpg
new file mode 100755 (executable)
index 0000000..cd66e83
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_161.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_162.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_162.jpg
new file mode 100755 (executable)
index 0000000..6af87e8
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_162.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_163.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_163.jpg
new file mode 100755 (executable)
index 0000000..1a903c3
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_163.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_164.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_164.jpg
new file mode 100755 (executable)
index 0000000..71694cf
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_164.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_165.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_165.jpg
new file mode 100755 (executable)
index 0000000..084c64a
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_165.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_166.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_166.jpg
new file mode 100755 (executable)
index 0000000..6554740
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_166.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_167.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_167.jpg
new file mode 100755 (executable)
index 0000000..ef2d248
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_167.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_168.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_168.jpg
new file mode 100755 (executable)
index 0000000..fda5636
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_168.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_169.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_169.jpg
new file mode 100755 (executable)
index 0000000..7b53b20
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_169.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_170.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_170.jpg
new file mode 100755 (executable)
index 0000000..271c69d
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_170.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_171.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_171.jpg
new file mode 100755 (executable)
index 0000000..a52ac34
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_171.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_172.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_172.jpg
new file mode 100755 (executable)
index 0000000..7438a7e
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_172.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_173.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_173.jpg
new file mode 100755 (executable)
index 0000000..d91d538
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_173.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_174.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_174.jpg
new file mode 100755 (executable)
index 0000000..3901ca5
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_174.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_175.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_175.jpg
new file mode 100755 (executable)
index 0000000..106900d
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_175.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_176.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_176.jpg
new file mode 100755 (executable)
index 0000000..c4a54bf
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_176.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_177.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_177.jpg
new file mode 100755 (executable)
index 0000000..d214f26
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_177.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_178.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_178.jpg
new file mode 100755 (executable)
index 0000000..be6cb55
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_178.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_179.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_179.jpg
new file mode 100755 (executable)
index 0000000..516faa1
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_179.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_180.jpg b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_180.jpg
new file mode 100755 (executable)
index 0000000..67bf870
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/004/gophertiles_180.jpg differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/005.txt b/modules/http2/sandbox/test/htdocs/test.example.org/005.txt
new file mode 100755 (executable)
index 0000000..5630165
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/005.txt differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/006.html b/modules/http2/sandbox/test/htdocs/test.example.org/006.html
new file mode 100755 (executable)
index 0000000..6b73025
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML> \r
+ <html>\r
+   <head>\r
+     <title>HTML/2.0 Test File: 006</title>\r
+     <link rel="stylesheet" type="text/css" href="006/006.css">\r
+     <script type="text/javascript" src="006/006.js"></script>\r
+   </head>\r
+   <body>\r
+     <h1>HTML/2.0 Test File: 006</h1>\r
+     <div class="listTitle">This page contains:\r
+            <ul class="listElements">\r
+                       <li>HTML\r
+                       <li>CSS\r
+                       <li>JavaScript\r
+               </ul> \r
+       </div>\r
+       <div class="listTitle">\r
+               <script type="text/javascript">\r
+                mainJavascript();\r
+               </script>\r
+       </div>\r
+   </body>\r
+</html>
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/006/006.css b/modules/http2/sandbox/test/htdocs/test.example.org/006/006.css
new file mode 100755 (executable)
index 0000000..de6aa5f
--- /dev/null
@@ -0,0 +1,21 @@
+@CHARSET "ISO-8859-1";\r
+body{\r
+       background:HoneyDew;\r
+}\r
+p{\r
+color:#0000FF;\r
+text-align:left;\r
+}\r
+\r
+h1{\r
+color:#FF0000;\r
+text-align:center;\r
+}\r
+\r
+.listTitle{\r
+       font-size:large;\r
+}\r
+\r
+.listElements{\r
+       color:#3366FF\r
+}
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/006/006.js b/modules/http2/sandbox/test/htdocs/test.example.org/006/006.js
new file mode 100755 (executable)
index 0000000..b450067
--- /dev/null
@@ -0,0 +1,31 @@
+/**\r
+ * JavaScript Functions File\r
+ */\r
+function returnDate()\r
+{\r
+  var currentDate;\r
+  currentDate=new Date();\r
+  var dateString=(currentDate.getMonth()+1)+'/'+currentDate.getDate()+'/'+currentDate.getFullYear();\r
+  return dateString;\r
+}\r
+\r
+function returnHour()\r
+{\r
+  var currentDate;\r
+  currentDate=new Date();\r
+  var hourString=currentDate.getHours()+':'+currentDate.getMinutes()+':'+currentDate.getSeconds();\r
+  return hourString; \r
+}\r
+\r
+function javaScriptMessage(){\r
+       return 'This section is generated under JavaScript:<br>';\r
+}\r
+\r
+function mainJavascript(){\r
+       document.write(javaScriptMessage())\r
+       document.write('<ul class="listElements">');\r
+       document.write('<li>Current date (dd/mm/yyyy): ' + returnDate());\r
+       document.write('<br>'); \r
+       document.write('<li>Current time (hh:mm:ss): '+returnHour());\r
+       document.write('</ul>');\r
+}
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/007.html b/modules/http2/sandbox/test/htdocs/test.example.org/007.html
new file mode 100755 (executable)
index 0000000..4db93e4
--- /dev/null
@@ -0,0 +1,21 @@
+<!DOCTYPE html>\r
+<html>\r
+<head>\r
+<meta charset="ISO-8859-1">\r
+<title>HTML/2.0 Test File: 007</title>\r
+</head>\r
+<body>\r
+       <h1>HTML/2.0 Test File: 007</h1>\r
+    <div><p>This page is used to send data from the client to the server:</p>\r
+               <FORM ACTION="007/007.py" METHOD="post" ENCTYPE="multipart/form-data">\r
+                       <input type="hidden" name="pageName" value="007.html">\r
+                       Name:<input type="text" name="pName" value="Write your name here." size="30" maxlength="30"><br>\r
+                       Age:<input type="text" name="pAge" value="00" size="2" maxlength="2"><br>\r
+                       Gender: Male<input type="radio" name="pGender" VALUE="Male">\r
+                                       Female<input type="radio" name="pGender" VALUE="Female"><br>\r
+                       <input type="submit" name="userForm" value="Send">\r
+                       <input type="reset" value="Clear">\r
+               </FORM> \r
+       </div>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/007/007.py b/modules/http2/sandbox/test/htdocs/test.example.org/007/007.py
new file mode 100755 (executable)
index 0000000..02b5466
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import cgi, sys
+import cgitb; cgitb.enable()
+
+print "Content-Type: text/html;charset=UTF-8"
+print
+
+print """\
+       <!DOCTYPE html><html><head>
+       <title>HTML/2.0 Test File: 007 (received data)</title></head>
+       <body><h1>HTML/2.0 Test File: 007</h1>"""
+
+# alternative output: parsed form params <-> plain POST body
+parseContent = True            # <-> False
+
+if parseContent:
+       print '<h2>Data processed:</h2><ul>'
+       form = cgi.FieldStorage()
+       for name in form:
+               print '<li>', name, ': ', form[name].value, '</li>'
+       print '</ul>'
+else:
+       print '<h2>POST data output:</h2><div><pre>'
+       data = sys.stdin.read()
+       print data
+       print '</pre></div>'
+       
+print '</body></html>'
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/009.py b/modules/http2/sandbox/test/htdocs/test.example.org/009.py
new file mode 100755 (executable)
index 0000000..8fd9095
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import cgi, sys, time
+import cgitb; cgitb.enable()
+
+print "Content-Type: text/html;charset=UTF-8"
+print
+
+print """\
+       <!DOCTYPE html><html><head>
+       <title>HTML/2.0 Test File: 009 (server time)</title></head>
+       <body><h1>HTML/2.0 Test File: 009</h1>
+    <p>60 seconds of server time, one by one.</p>"""
+
+for i in range(60):
+       s = time.strftime("%Y-%m-%d %H:%M:%S")
+       print "<div>", s, "</div>"
+       sys.stdout.flush()
+       time.sleep(1)
+
+print "<p>done.</p></body></html>"
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/files/empty.txt b/modules/http2/sandbox/test/htdocs/test.example.org/files/empty.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/funky.png b/modules/http2/sandbox/test/htdocs/test.example.org/funky.png
new file mode 100755 (executable)
index 0000000..8d6ba15
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/funky.png differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/hello.py b/modules/http2/sandbox/test/htdocs/test.example.org/hello.py
new file mode 100644 (file)
index 0000000..c96c78c
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+import os
+
+print "Content-Type: text/html"
+print
+print """\
+<html>
+<body>
+<h2>Hello World!</h2>"""
+print "SSL_PROTOCOL=" + os.getenv('SSL_PROTOCOL', '')
+print """</body>
+</html>"""
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/index.html b/modules/http2/sandbox/test/htdocs/test.example.org/index.html
new file mode 100644 (file)
index 0000000..aa11d4f
--- /dev/null
@@ -0,0 +1,45 @@
+<html>
+    <head>
+        <title>mod_h2 test site</title>
+    </head>
+    <body>
+        <h1>mod_h2 test site</h1>
+        <p></p>
+        <h2>served directly</h2>
+        <ul>
+            <li><a href="001.html">01: html</a></li>
+            <li><a href="002.jpg">02: image</a></li>
+            <li><a href="003.html">03: html+image</a></li>
+            <li><a href="004.html">04: tiled image</a></li>
+            <li><a href="005.txt">05: large text</a></li>
+            <li><a href="006.html">06: html/js/css</a></li>
+            <li><a href="007.html">07: form submit</a></li>
+            <li><a href="upload.py">08: upload</a></li>
+            <li><a href="009.py">09: small chunks</a></li>
+        </ul>
+        <h2>mod_proxyied</h2>
+        <ul>
+            <li><a href="proxy/001.html">01: html</a></li>
+            <li><a href="proxy/002.jpg">02: image</a></li>
+            <li><a href="proxy/003.html">03: html+image</a></li>
+            <li><a href="proxy/004.html">04: tiled image</a></li>
+            <li><a href="proxy/005.txt">05: large text</a></li>
+            <li><a href="proxy/006.html">06: html/js/css</a></li>
+            <li><a href="proxy/007.html">07: form submit</a></li>
+            <li><a href="proxy/upload.py">08: upload</a></li>
+            <li><a href="proxy/009.py">09: small chunks</a></li>
+        </ul>
+        <h2>mod_rewritten</h2>
+        <ul>
+            <li><a href="rewrite/001.html">01: html</a></li>
+            <li><a href="rewrite/002.jpg">02: image</a></li>
+            <li><a href="rewrite/003.html">03: html+image</a></li>
+            <li><a href="rewrite/004.html">04: tiled image</a></li>
+            <li><a href="rewrite/005.txt">05: large text</a></li>
+            <li><a href="rewrite/006.html">06: html/js/css</a></li>
+            <li><a href="rewrite/007.html">07: form submit</a></li>
+            <li><a href="rewrite/upload.py">08: upload</a></li>
+            <li><a href="rewrite/009.py">09: small chunks</a></li>
+        </ul>
+    </body>
+</html>
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/info.php b/modules/http2/sandbox/test/htdocs/test.example.org/info.php
new file mode 100644 (file)
index 0000000..640e4f2
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+    phpinfo();
+?>
\ No newline at end of file
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/necho.py b/modules/http2/sandbox/test/htdocs/test.example.org/necho.py
new file mode 100644 (file)
index 0000000..787c996
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+import cgi, os
+import cgitb; cgitb.enable()
+
+status = '200 Ok'
+
+try:
+    form = cgi.FieldStorage()
+    
+    # A nested FieldStorage instance holds the file
+    count = form['count']
+    text = form['text']
+    
+    # Test if the file was uploaded
+    if int(count.value):
+        print "Status: 200"
+        print """\
+Content-Type: text/plain\n"""
+        i = 0;
+        for i in range(0, int(count.value)):
+            print """%s""" % (text.value,)
+
+    else:
+        print "Status: 400 Parameter Missing"
+        print """\
+    Content-Type: text/html\n
+    <html><body>
+    <p>No count was specified: %s</p>
+    </body></html>""" % (count.value,)
+
+except KeyError:
+    print "Status: 200 Ok"
+    print """\
+    Content-Type: text/html\n
+    <html><body>
+    Echo <form method="POST" enctype="application/x-www-form-urlencoded">
+    <input type="text" name="count">
+    <input type="text" name="text">
+    <button type="submit">Echo</button></form>
+    </body></html>"""
+    pass
+
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/sei.png b/modules/http2/sandbox/test/htdocs/test.example.org/sei.png
new file mode 100644 (file)
index 0000000..91ad9e1
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/sei.png differ
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/upload.py b/modules/http2/sandbox/test/htdocs/test.example.org/upload.py
new file mode 100644 (file)
index 0000000..71880f1
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+import cgi, os
+import cgitb; cgitb.enable()
+
+status = '200 Ok'
+
+try: # Windows needs stdio set for binary mode.
+    import msvcrt
+    msvcrt.setmode (0, os.O_BINARY) # stdin  = 0
+    msvcrt.setmode (1, os.O_BINARY) # stdout = 1
+except ImportError:
+    pass
+
+form = cgi.FieldStorage()
+
+# Test if the file was uploaded
+if 'file' in form:
+    # A nested FieldStorage instance holds the file
+    fileitem = form['file']
+    
+    # strip leading path from file name to avoid directory traversal attacks
+    fn = os.path.basename(fileitem.filename)
+    open('./files/' + fn, 'wb').write(fileitem.file.read())
+    message = 'The file "' + fn + '" was uploaded successfully'
+
+elif 'remove' in form:
+    remove = form['remove'].value
+    try:
+        fn = os.path.basename(remove)
+        os.remove('./files/' + fn)
+        message = 'The file "' + fn + '" was removed successfully'
+    except OSError, e:
+        message = 'Error removing ' + fn + ': ' + e.strerror
+        status = '404 File Not Found'
+else:
+    message = '''\
+        Upload File<form method="POST" enctype="multipart/form-data">
+        <input type="file" name="file">
+        <button type="submit">Upload</button></form>
+        '''
+
+print "Status: %s" % (status,)
+print """\
+    Content-Type: text/html\n
+    <html><body>
+    <p>%s</p>
+    </body></html>""" % (message,)
diff --git a/modules/http2/sandbox/test/htdocs/test.example.org/xxx-1.0.2a.tar.gz b/modules/http2/sandbox/test/htdocs/test.example.org/xxx-1.0.2a.tar.gz
new file mode 100644 (file)
index 0000000..f025d43
Binary files /dev/null and b/modules/http2/sandbox/test/htdocs/test.example.org/xxx-1.0.2a.tar.gz differ
diff --git a/modules/http2/sandbox/test/load-urls-1.txt b/modules/http2/sandbox/test/load-urls-1.txt
new file mode 100644 (file)
index 0000000..0256782
--- /dev/null
@@ -0,0 +1,181 @@
+SUBST_AUTH_SUBST/004.html
+SUBST_AUTH_SUBST/004/gophertiles.jpg
+SUBST_AUTH_SUBST/004/gophertiles_002.jpg
+SUBST_AUTH_SUBST/004/gophertiles_003.jpg
+SUBST_AUTH_SUBST/004/gophertiles_004.jpg
+SUBST_AUTH_SUBST/004/gophertiles_005.jpg
+SUBST_AUTH_SUBST/004/gophertiles_006.jpg
+SUBST_AUTH_SUBST/004/gophertiles_007.jpg
+SUBST_AUTH_SUBST/004/gophertiles_008.jpg
+SUBST_AUTH_SUBST/004/gophertiles_009.jpg
+SUBST_AUTH_SUBST/004/gophertiles_010.jpg
+SUBST_AUTH_SUBST/004/gophertiles_011.jpg
+SUBST_AUTH_SUBST/004/gophertiles_012.jpg
+SUBST_AUTH_SUBST/004/gophertiles_013.jpg
+SUBST_AUTH_SUBST/004/gophertiles_014.jpg
+SUBST_AUTH_SUBST/004/gophertiles_015.jpg
+SUBST_AUTH_SUBST/004/gophertiles_016.jpg
+SUBST_AUTH_SUBST/004/gophertiles_017.jpg
+SUBST_AUTH_SUBST/004/gophertiles_018.jpg
+SUBST_AUTH_SUBST/004/gophertiles_019.jpg
+SUBST_AUTH_SUBST/004/gophertiles_020.jpg
+SUBST_AUTH_SUBST/004/gophertiles_021.jpg
+SUBST_AUTH_SUBST/004/gophertiles_022.jpg
+SUBST_AUTH_SUBST/004/gophertiles_023.jpg
+SUBST_AUTH_SUBST/004/gophertiles_024.jpg
+SUBST_AUTH_SUBST/004/gophertiles_025.jpg
+SUBST_AUTH_SUBST/004/gophertiles_026.jpg
+SUBST_AUTH_SUBST/004/gophertiles_027.jpg
+SUBST_AUTH_SUBST/004/gophertiles_028.jpg
+SUBST_AUTH_SUBST/004/gophertiles_029.jpg
+SUBST_AUTH_SUBST/004/gophertiles_030.jpg
+SUBST_AUTH_SUBST/004/gophertiles_031.jpg
+SUBST_AUTH_SUBST/004/gophertiles_032.jpg
+SUBST_AUTH_SUBST/004/gophertiles_033.jpg
+SUBST_AUTH_SUBST/004/gophertiles_034.jpg
+SUBST_AUTH_SUBST/004/gophertiles_035.jpg
+SUBST_AUTH_SUBST/004/gophertiles_036.jpg
+SUBST_AUTH_SUBST/004/gophertiles_037.jpg
+SUBST_AUTH_SUBST/004/gophertiles_038.jpg
+SUBST_AUTH_SUBST/004/gophertiles_039.jpg
+SUBST_AUTH_SUBST/004/gophertiles_040.jpg
+SUBST_AUTH_SUBST/004/gophertiles_041.jpg
+SUBST_AUTH_SUBST/004/gophertiles_042.jpg
+SUBST_AUTH_SUBST/004/gophertiles_043.jpg
+SUBST_AUTH_SUBST/004/gophertiles_044.jpg
+SUBST_AUTH_SUBST/004/gophertiles_045.jpg
+SUBST_AUTH_SUBST/004/gophertiles_046.jpg
+SUBST_AUTH_SUBST/004/gophertiles_047.jpg
+SUBST_AUTH_SUBST/004/gophertiles_048.jpg
+SUBST_AUTH_SUBST/004/gophertiles_049.jpg
+SUBST_AUTH_SUBST/004/gophertiles_050.jpg
+SUBST_AUTH_SUBST/004/gophertiles_051.jpg
+SUBST_AUTH_SUBST/004/gophertiles_052.jpg
+SUBST_AUTH_SUBST/004/gophertiles_053.jpg
+SUBST_AUTH_SUBST/004/gophertiles_054.jpg
+SUBST_AUTH_SUBST/004/gophertiles_055.jpg
+SUBST_AUTH_SUBST/004/gophertiles_056.jpg
+SUBST_AUTH_SUBST/004/gophertiles_057.jpg
+SUBST_AUTH_SUBST/004/gophertiles_058.jpg
+SUBST_AUTH_SUBST/004/gophertiles_059.jpg
+SUBST_AUTH_SUBST/004/gophertiles_060.jpg
+SUBST_AUTH_SUBST/004/gophertiles_061.jpg
+SUBST_AUTH_SUBST/004/gophertiles_062.jpg
+SUBST_AUTH_SUBST/004/gophertiles_063.jpg
+SUBST_AUTH_SUBST/004/gophertiles_064.jpg
+SUBST_AUTH_SUBST/004/gophertiles_065.jpg
+SUBST_AUTH_SUBST/004/gophertiles_066.jpg
+SUBST_AUTH_SUBST/004/gophertiles_067.jpg
+SUBST_AUTH_SUBST/004/gophertiles_068.jpg
+SUBST_AUTH_SUBST/004/gophertiles_069.jpg
+SUBST_AUTH_SUBST/004/gophertiles_070.jpg
+SUBST_AUTH_SUBST/004/gophertiles_071.jpg
+SUBST_AUTH_SUBST/004/gophertiles_072.jpg
+SUBST_AUTH_SUBST/004/gophertiles_073.jpg
+SUBST_AUTH_SUBST/004/gophertiles_074.jpg
+SUBST_AUTH_SUBST/004/gophertiles_075.jpg
+SUBST_AUTH_SUBST/004/gophertiles_076.jpg
+SUBST_AUTH_SUBST/004/gophertiles_077.jpg
+SUBST_AUTH_SUBST/004/gophertiles_078.jpg
+SUBST_AUTH_SUBST/004/gophertiles_079.jpg
+SUBST_AUTH_SUBST/004/gophertiles_080.jpg
+SUBST_AUTH_SUBST/004/gophertiles_081.jpg
+SUBST_AUTH_SUBST/004/gophertiles_082.jpg
+SUBST_AUTH_SUBST/004/gophertiles_083.jpg
+SUBST_AUTH_SUBST/004/gophertiles_084.jpg
+SUBST_AUTH_SUBST/004/gophertiles_085.jpg
+SUBST_AUTH_SUBST/004/gophertiles_086.jpg
+SUBST_AUTH_SUBST/004/gophertiles_087.jpg
+SUBST_AUTH_SUBST/004/gophertiles_088.jpg
+SUBST_AUTH_SUBST/004/gophertiles_089.jpg
+SUBST_AUTH_SUBST/004/gophertiles_090.jpg
+SUBST_AUTH_SUBST/004/gophertiles_091.jpg
+SUBST_AUTH_SUBST/004/gophertiles_092.jpg
+SUBST_AUTH_SUBST/004/gophertiles_093.jpg
+SUBST_AUTH_SUBST/004/gophertiles_094.jpg
+SUBST_AUTH_SUBST/004/gophertiles_095.jpg
+SUBST_AUTH_SUBST/004/gophertiles_096.jpg
+SUBST_AUTH_SUBST/004/gophertiles_097.jpg
+SUBST_AUTH_SUBST/004/gophertiles_098.jpg
+SUBST_AUTH_SUBST/004/gophertiles_099.jpg
+SUBST_AUTH_SUBST/004/gophertiles_100.jpg
+SUBST_AUTH_SUBST/004/gophertiles_101.jpg
+SUBST_AUTH_SUBST/004/gophertiles_102.jpg
+SUBST_AUTH_SUBST/004/gophertiles_103.jpg
+SUBST_AUTH_SUBST/004/gophertiles_104.jpg
+SUBST_AUTH_SUBST/004/gophertiles_105.jpg
+SUBST_AUTH_SUBST/004/gophertiles_106.jpg
+SUBST_AUTH_SUBST/004/gophertiles_107.jpg
+SUBST_AUTH_SUBST/004/gophertiles_108.jpg
+SUBST_AUTH_SUBST/004/gophertiles_109.jpg
+SUBST_AUTH_SUBST/004/gophertiles_110.jpg
+SUBST_AUTH_SUBST/004/gophertiles_111.jpg
+SUBST_AUTH_SUBST/004/gophertiles_112.jpg
+SUBST_AUTH_SUBST/004/gophertiles_113.jpg
+SUBST_AUTH_SUBST/004/gophertiles_114.jpg
+SUBST_AUTH_SUBST/004/gophertiles_115.jpg
+SUBST_AUTH_SUBST/004/gophertiles_116.jpg
+SUBST_AUTH_SUBST/004/gophertiles_117.jpg
+SUBST_AUTH_SUBST/004/gophertiles_118.jpg
+SUBST_AUTH_SUBST/004/gophertiles_119.jpg
+SUBST_AUTH_SUBST/004/gophertiles_120.jpg
+SUBST_AUTH_SUBST/004/gophertiles_121.jpg
+SUBST_AUTH_SUBST/004/gophertiles_122.jpg
+SUBST_AUTH_SUBST/004/gophertiles_123.jpg
+SUBST_AUTH_SUBST/004/gophertiles_124.jpg
+SUBST_AUTH_SUBST/004/gophertiles_125.jpg
+SUBST_AUTH_SUBST/004/gophertiles_126.jpg
+SUBST_AUTH_SUBST/004/gophertiles_127.jpg
+SUBST_AUTH_SUBST/004/gophertiles_128.jpg
+SUBST_AUTH_SUBST/004/gophertiles_129.jpg
+SUBST_AUTH_SUBST/004/gophertiles_130.jpg
+SUBST_AUTH_SUBST/004/gophertiles_131.jpg
+SUBST_AUTH_SUBST/004/gophertiles_132.jpg
+SUBST_AUTH_SUBST/004/gophertiles_133.jpg
+SUBST_AUTH_SUBST/004/gophertiles_134.jpg
+SUBST_AUTH_SUBST/004/gophertiles_135.jpg
+SUBST_AUTH_SUBST/004/gophertiles_136.jpg
+SUBST_AUTH_SUBST/004/gophertiles_137.jpg
+SUBST_AUTH_SUBST/004/gophertiles_138.jpg
+SUBST_AUTH_SUBST/004/gophertiles_139.jpg
+SUBST_AUTH_SUBST/004/gophertiles_140.jpg
+SUBST_AUTH_SUBST/004/gophertiles_141.jpg
+SUBST_AUTH_SUBST/004/gophertiles_142.jpg
+SUBST_AUTH_SUBST/004/gophertiles_143.jpg
+SUBST_AUTH_SUBST/004/gophertiles_144.jpg
+SUBST_AUTH_SUBST/004/gophertiles_145.jpg
+SUBST_AUTH_SUBST/004/gophertiles_146.jpg
+SUBST_AUTH_SUBST/004/gophertiles_147.jpg
+SUBST_AUTH_SUBST/004/gophertiles_148.jpg
+SUBST_AUTH_SUBST/004/gophertiles_149.jpg
+SUBST_AUTH_SUBST/004/gophertiles_150.jpg
+SUBST_AUTH_SUBST/004/gophertiles_151.jpg
+SUBST_AUTH_SUBST/004/gophertiles_152.jpg
+SUBST_AUTH_SUBST/004/gophertiles_153.jpg
+SUBST_AUTH_SUBST/004/gophertiles_154.jpg
+SUBST_AUTH_SUBST/004/gophertiles_155.jpg
+SUBST_AUTH_SUBST/004/gophertiles_156.jpg
+SUBST_AUTH_SUBST/004/gophertiles_157.jpg
+SUBST_AUTH_SUBST/004/gophertiles_158.jpg
+SUBST_AUTH_SUBST/004/gophertiles_159.jpg
+SUBST_AUTH_SUBST/004/gophertiles_160.jpg
+SUBST_AUTH_SUBST/004/gophertiles_161.jpg
+SUBST_AUTH_SUBST/004/gophertiles_162.jpg
+SUBST_AUTH_SUBST/004/gophertiles_163.jpg
+SUBST_AUTH_SUBST/004/gophertiles_164.jpg
+SUBST_AUTH_SUBST/004/gophertiles_165.jpg
+SUBST_AUTH_SUBST/004/gophertiles_166.jpg
+SUBST_AUTH_SUBST/004/gophertiles_167.jpg
+SUBST_AUTH_SUBST/004/gophertiles_168.jpg
+SUBST_AUTH_SUBST/004/gophertiles_169.jpg
+SUBST_AUTH_SUBST/004/gophertiles_170.jpg
+SUBST_AUTH_SUBST/004/gophertiles_171.jpg
+SUBST_AUTH_SUBST/004/gophertiles_172.jpg
+SUBST_AUTH_SUBST/004/gophertiles_173.jpg
+SUBST_AUTH_SUBST/004/gophertiles_174.jpg
+SUBST_AUTH_SUBST/004/gophertiles_175.jpg
+SUBST_AUTH_SUBST/004/gophertiles_176.jpg
+SUBST_AUTH_SUBST/004/gophertiles_177.jpg
+SUBST_AUTH_SUBST/004/gophertiles_178.jpg
+SUBST_AUTH_SUBST/004/gophertiles_179.jpg
+SUBST_AUTH_SUBST/004/gophertiles_180.jpg
diff --git a/modules/http2/sandbox/test/test_alt_host.sh b/modules/http2/sandbox/test/test_alt_host.sh
new file mode 100644 (file)
index 0000000..079dd31
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source test_common.sh
+echo "alt host access: $@"
+
+################################################################################
+# check access to other hosts on same connection
+################################################################################
+
+# The correct answer is 421 and mod_h2 will created if once the SSL parse 
+# request filter is no longer strict on SNI name checking. See
+# https://bz.apache.org/bugzilla/show_bug.cgi?id=58007#c9
+#
+MISDIR_STATUS="421 Misdirected Request"
+#MISDIR_STATUS="400 Bad Request"
+
+nghttp_check_content index.html "noh2 host" -H'Host: noh2.example.org' <<EOF
+[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)
+Some requests were not processed. total=1, processed=0
+EOF
+
+curl_check_content index.html "noh2 host" -H'Host: noh2.example.org' <<EOF
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>$MISDIR_STATUS</title>
+</head><body>
+<h1>Misdirected Request</h1>
+<p>The client needs to use a new connection for this 
+request as it does not match the SNI name used.</p>
+</body></html>
+EOF
+
diff --git a/modules/http2/sandbox/test/test_common.sh b/modules/http2/sandbox/test/test_common.sh
new file mode 100644 (file)
index 0000000..e940160
--- /dev/null
@@ -0,0 +1,249 @@
+#!/bin/bash
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# common test functions
+#
+
+URL_PREFIX="$1"
+OPT_DIRECT="$2"
+AUTH="${URL_PREFIX#*://}"
+HOST="${AUTH%%:*}"
+URL_SCHEME="${URL_PREFIX%%:*}"
+URL_PATH="/${AUTH#*/}"
+if [ "$URL_PATH" = "/$AUTH" ]; then
+    URL_PATH=""
+fi
+
+INSTALL_DIR="../install"
+BIN_DIR="${INSTALL_DIR}/bin"
+if [ "${HOST#*.}" = 'example.org' ]; then
+    DOC_ROOT="htdocs/test.example.org"
+else 
+    DOC_ROOT="htdocs/${HOST}"
+fi
+
+GEN="gen"
+TMP="$GEN/tmp"
+
+CURL="${BIN_DIR}/curl  -sk --resolv ${HOST#*://}:127.0.0.1"
+NGHTTP="${BIN_DIR}/nghttp"
+
+
+fail() {
+    echo "$@"
+    exit 1
+}
+
+case "$OPT_DIRECT" in
+  "direct")
+        ARG_UPGRADE=""
+        ;;
+    *)  
+        ARG_UPGRADE=" -u"
+        ;;
+esac
+
+curl_check_doc() {
+    DOC="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"
+    echo -n " * curl /$DOC: $MSG..."
+    rm -rf $TMP
+    mkdir -p $TMP
+    ${CURL} $ARGS $URL_PREFIX/$DOC > $TMP/$DOC 2>&1 || fail
+    diff  $DOC_ROOT/$DOC $TMP/$DOC || fail
+    echo ok.
+}
+
+nghttp_check_doc() {
+    DOC="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"$ARG_UPGRADE
+    echo -n " * nghttp /$DOC: $MSG..."
+    rm -rf $TMP &&
+    mkdir -p $TMP &&
+    ${NGHTTP} $ARGS $URL_PREFIX/$DOC > $TMP/$DOC 2>&1 || fail
+    diff  $DOC_ROOT/$DOC $TMP/$DOC || fail
+    echo ok.
+}
+
+nghttp_check_assets() {
+    DOC="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"$ARG_UPGRADE
+    echo -n " * nghttp /$DOC: $MSG..."
+    rm -rf $TMP &&
+    mkdir -p $TMP &&
+    sort > $TMP/reference
+    ${NGHTTP} -ans $ARGS $URL_PREFIX/$DOC > $TMP/out 2>&1 || fail
+    fgrep " /" $TMP/out | while read id begin end dur stat size path; do
+        echo "$path $size $stat"
+    done | sort > $TMP/output || fail
+    diff $TMP/reference $TMP/output  || fail
+    echo ok.
+}
+
+nghttp_check_content() {
+    DOC="$1"; shift;
+    MSG="$1"; shift;
+    rm -rf $TMP
+    mkdir -p $TMP
+    cat > $TMP/expected
+    echo -n " * nghttp /$DOC: $MSG..."
+    ${NGHTTP} "$@" $URL_PREFIX/$DOC > $TMP/$DOC 2>&1 || fail
+    diff  $TMP/expected $TMP/$DOC || fail
+    echo ok.
+}
+
+
+curl_check_content() {
+    DOC="$1"; shift;
+    MSG="$1"; shift;
+    rm -rf $TMP
+    mkdir -p $TMP
+    cat > $TMP/expected
+    echo -n " * curl /$DOC: $MSG..."
+    ${CURL} "$@" $URL_PREFIX/$DOC > $TMP/$DOC 2>&1 || fail
+    diff  $TMP/expected $TMP/$DOC || fail
+    echo ok.
+}
+
+curl_check_redir() {
+    DOC="$1"; shift;
+    REF_DOC="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"
+    echo -n " * curl redir /$DOC: $MSG..."
+    rm -rf $TMP
+    mkdir -p $TMP
+    ${CURL} -D - $ARGS $URL_PREFIX/$DOC >$TMP/redir.out || fail
+    LOCATION=$( fgrep -i 'location:' $TMP/redir.out | sed -e "s,.*$URL_PREFIX/,," | tr -d '\r\n' )
+    test "$REF_DOC" != "$LOCATION" && fail "expected redirect to >>>$REF_DOC<<<, found >>>$LOCATION<<<"
+    ${CURL} $ARGS $URL_PREFIX/$LOCATION >$TMP/$LOCATION || fail
+    diff  $DOC_ROOT/$REF_DOC $TMP/$LOCATION || fail
+    echo ok.
+}
+
+curl_check_necho() {
+    COUNT="$1"; shift;
+    TEXT="$1"; shift;
+    REF="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"
+    rm -rf $TMP
+    mkdir -p $TMP
+    echo -n " * curl /necho.py?count=$COUNT&text=$TEXT..."
+    ${CURL} $ARGS -F count="$COUNT" -F text="$TEXT" $URL_PREFIX/necho.py > $TMP/echo 2>&1 || fail
+    diff  $REF $TMP/echo || fail
+    echo ok.
+}
+
+curl_post_file() {
+    DOC="$1"; shift;
+    FILE="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"
+    fname="$(basename $FILE)"
+    rm -rf $TMP
+    mkdir -p $TMP
+    echo -n " * curl /$DOC: $MSG..."
+    ${CURL} $ARGS --form file=@"$FILE" $URL_PREFIX/$DOC > $TMP/$DOC 2>&1 || fail "error uploading $fname"
+    ${CURL} $ARGS $URL_PREFIX/files/"$fname" > $TMP/data.down 2>&1 || fail "error downloding $fname"
+    diff  $FILE $TMP/data.down || fail
+    echo ok.
+}
+
+curl_post_data() {
+    DOC="$1"; shift;
+    FILE="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"
+    fname="$(basename $FILE)"
+    rm -rf $TMP
+    mkdir -p $TMP
+    echo -n " * curl /$DOC: $MSG..."
+    ${CURL} $ARGS --form file=@"$FILE" $URL_PREFIX/$DOC > $TMP/$DOC 2>&1 || fail
+    ${CURL} $ARGS $URL_PREFIX/files/"$fname" > $TMP/data.down 2>&1 || fail
+    diff  $FILE $TMP/data.down || fail
+    echo ok.
+}
+
+nghttp_remove_file() {
+    DOC="$1"; shift;
+    FILE="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"$ARG_UPGRADE
+    fname="$(basename $FILE)"
+    rm -rf $TMP
+    mkdir -p $TMP
+    cat > $TMP/updata <<EOF
+--DSAJKcd9876
+Content-Disposition: form-data; name="remove";
+Content-Type: text/plain
+
+$fname
+--DSAJKcd9876--
+EOF
+    echo -n " * nghttp /$DOC: rm $fname..."
+    ${NGHTTP} -v $ARGS --data=$TMP/updata -H'Content-Type: multipart/form-data; boundary=DSAJKcd9876' $URL_PREFIX/$DOC > $TMP/$DOC || fail "error removing $fname"
+    echo ok.
+}
+
+nghttp_post_file() {
+    DOC="$1"; shift;
+    FILE="$1"; shift;
+    MSG="$1"; shift;
+    ARGS="$@"$ARG_UPGRADE
+    fname="$(basename $FILE)"
+    rm -rf $TMP
+    mkdir -p $TMP
+    cat > $TMP/updata <<EOF
+--DSAJKcd9876
+Content-Disposition: form-data; name="xxx"; filename="xxxxx"
+Content-Type: text/plain
+
+testing mod_h2
+--DSAJKcd9876
+Content-Disposition: form-data; name="file"; filename="$fname"
+Content-Type: application/octet-stream
+Content-Transfer-Encoding: binary
+
+EOF
+    cat $FILE >> $TMP/updata || fail "error reading $FILE"
+    echo >> $TMP/updata <<EOF
+--DSAJKcd9876--
+EOF
+    echo -n " * nghttp /$DOC: $MSG..."
+    ${NGHTTP} -v $ARGS --data=$TMP/updata -H'Content-Type: multipart/form-data; boundary=DSAJKcd9876' $URL_PREFIX/$DOC > $TMP/$DOC || fail "error uploading $fname"
+
+    ${NGHTTP} $ARG_UPGRADE $URL_PREFIX/files/"$fname" > $TMP/data.down || fail "error downloding $fname"
+    diff  $FILE $TMP/data.down || fail
+    echo ok.
+}
+
+curl_check_altsvc() {
+    DOC="$1"; shift;
+    EXP_ALT_SVC="$1"; shift;
+    MSG="$1"; shift;
+    mkdir -p $TMP
+    echo -n " * curl check alt_svc at /$DOC..."
+    ${CURL} "$@" -D $TMP/headers $URL_PREFIX/$DOC > /dev/null 2>&1 || fail
+    alt_svc="$( fgrep -i 'Alt-Svc: ' $TMP/headers | tr -d "\r\n" )"
+    alt_svc="${alt_svc#*: }"
+    test "$EXP_ALT_SVC" = "$alt_svc" || fail "failed. Expected '$EXP_ALT_SVC', got '$alt_svc'"
+    echo ok.
+}
+
diff --git a/modules/http2/sandbox/test/test_curl_altsvc.sh b/modules/http2/sandbox/test/test_curl_altsvc.sh
new file mode 100644 (file)
index 0000000..e2e24f3
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+HTTP_URL="$1"
+HTTPS_URL="$2"
+
+source test_common.sh
+echo "curl ALT-SVC on: $@"
+
+URL_PREFIX="$HTTP_URL"
+curl_check_altsvc index.html '' --http1.1
+curl_check_altsvc index.html '' "http/1.1, signal used"             --http1.1 -H'Alt-Svc-Used: 1'
+curl_check_altsvc index.html '' "http/2"                            --http2
+
+URL_PREFIX="$HTTPS_URL"
+curl_check_altsvc index.html 'h2=":12346", h2c=":12345", h2="mod-h2.greenbytes.de:12346"' "http/1.1" --http1.1
+curl_check_altsvc index.html '' "http/2" --http2
diff --git a/modules/http2/sandbox/test/test_curl_get.sh b/modules/http2/sandbox/test/test_curl_get.sh
new file mode 100644 (file)
index 0000000..7f76872
--- /dev/null
@@ -0,0 +1,132 @@
+#!/bin/bash
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source test_common.sh
+echo "curl GET on: $@"
+
+################################################################################
+# check content of resources via different methods
+################################################################################
+curl_check_doc index.html "default"
+curl_check_doc index.html "http/1.1" --http1.1
+curl_check_doc index.html "http2"    --http2
+
+################################################################################
+# check some redir handling
+################################################################################
+curl_check_doc xxx-1.0.2a.tar.gz  "http2"  --http2
+
+if [ "$URL_PATH" = "" ]; then
+    curl_check_redir latest.tar.gz  xxx-1.0.2a.tar.gz  "http2"  --http2
+fi
+
+################################################################################
+# check cgi generated content
+################################################################################
+if [ "$URL_SCHEME" = "https" ]; then
+    CONTENT="<html>
+<body>
+<h2>Hello World!</h2>
+SSL_PROTOCOL=TLSv1.2
+</body>
+</html>"
+else
+    CONTENT="<html>
+<body>
+<h2>Hello World!</h2>
+SSL_PROTOCOL=
+</body>
+</html>"
+fi
+
+curl_check_content hello.py "default" <<EOF
+$CONTENT
+EOF
+
+curl_check_content hello.py "http/1.1" --http1.1 <<EOF
+$CONTENT
+EOF
+
+curl_check_content hello.py "http2"    --http2 <<EOF
+$CONTENT
+EOF
+
+
+curl_check_content upload.py "http/1.1" --http1.1 <<EOF
+    <html><body>
+    <p>        Upload File<form method="POST" enctype="multipart/form-data">
+        <input type="file" name="file">
+        <button type="submit">Upload</button></form>
+        </p>
+    </body></html>
+EOF
+
+curl_check_content upload.py "http2"    --http2 <<EOF
+    <html><body>
+    <p>        Upload File<form method="POST" enctype="multipart/form-data">
+        <input type="file" name="file">
+        <button type="submit">Upload</button></form>
+        </p>
+    </body></html>
+EOF
+
+
+################################################################################
+# check chunked content from cgi
+################################################################################
+
+if [ ! -f $GEN/necho-100 ]; then
+i=0; while [ $i -lt 10 ]; do
+echo "0123456789"
+i=$[ i + 1 ]
+done > $GEN/necho-100
+fi
+
+if [ ! -f $GEN/necho-1k ]; then
+i=0; while [ $i -lt 10 ]; do
+cat $GEN/necho-100
+i=$[ i + 1 ]
+done > $GEN/necho-1k
+fi
+
+if [ ! -f $GEN/necho-10k ]; then
+i=0; while [ $i -lt 10 ]; do
+cat $GEN/necho-1k
+i=$[ i + 1 ]
+done > $GEN/necho-10k
+fi
+
+if [ ! -f $GEN/necho-100k ]; then
+i=0; while [ $i -lt 10 ]; do
+cat $GEN/necho-10k
+i=$[ i + 1 ]
+done > $GEN/necho-100k
+fi
+
+if [ ! -f $GEN/necho-1m ]; then
+i=0; while [ $i -lt 10 ]; do
+cat $GEN/necho-100k
+i=$[ i + 1 ]
+done > $GEN/necho-1m
+fi
+
+curl_check_necho 10 "0123456789" $GEN/necho-100 "http/2" --http2
+curl_check_necho 100 "0123456789" $GEN/necho-1k "http/2" --http2
+curl_check_necho 1000 "0123456789" $GEN/necho-10k "http/2" --http2
+curl_check_necho 10000 "0123456789" $GEN/necho-100k "http/2" --http2
+curl_check_necho 100000 "0123456789" $GEN/necho-1m "http/2" --http2
+
+
diff --git a/modules/http2/sandbox/test/test_curl_post.sh b/modules/http2/sandbox/test/test_curl_post.sh
new file mode 100644 (file)
index 0000000..a8b275d
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/bash
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source test_common.sh
+echo "curl POST on: $@"
+
+CHR100="012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
+"
+
+if [ ! -f $GEN/data-1k ]; then
+    i=0; while [ $i -lt 10 ]; do
+        echo -n "$CHR100"
+        i=$[ i + 1 ]
+    done > $GEN/data-1k
+fi
+
+if [ ! -f $GEN/data-10k ]; then
+    i=0; while [ $i -lt 10 ]; do
+        cat $GEN/data-1k
+        i=$[ i + 1 ]
+    done  > $GEN/data-10k
+fi
+
+if [ ! -f $GEN/data-100k ]; then
+    i=0; while [ $i -lt 10 ]; do
+        cat $GEN/data-10k
+        i=$[ i + 1 ]
+    done > $GEN/data-100k
+fi
+
+if [ ! -f $GEN/data-1m ]; then
+    i=0; while [ $i -lt 10 ]; do
+        cat $GEN/data-100k
+        i=$[ i + 1 ]
+    done > $GEN/data-1m
+fi
+
+# just a check that things are working
+curl_post_data upload.py $GEN/data-1k "file upload via http/1.1" --http1.1
+
+# on curl 7.40.0 and earlier, there will be a delay before the upload
+# commences. Fix is underway, thanks @badger!
+# Caveat: on h2c, the connection will not be upgraded, since curl sends
+# the POST as first request and mod_h2 does not upgrade on requests with
+# content. Currently we have no means to check that his is happening.
+# on curl 7.41.0 and earlier, the transfer of the upload data will be
+# extremely slow. Fix will be in 7.42.0, thanks @bagder!
+#
+# disable until 7.42.0 arrives....
+#curl_post_data upload.py $GEN/data-1k "1k file upload via http/2" --http2
+#curl_post_data upload.py $GEN/data-10k "10k file upload via http/2" --http2
+#curl_post_data upload.py $GEN/data-100k "100k file upload via http/2" --http2
+#curl_post_data upload.py $GEN/data-1m "1m file upload via http/2" --http2
+
+
+
+
+
diff --git a/modules/http2/sandbox/test/test_nghttp_get.sh b/modules/http2/sandbox/test/test_nghttp_get.sh
new file mode 100644 (file)
index 0000000..8769ef7
--- /dev/null
@@ -0,0 +1,279 @@
+#!/bin/bash
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source test_common.sh
+echo "nghttp GET on: $@"
+
+################################################################################
+# check content of resources via different methods
+################################################################################
+nghttp_check_doc index.html "default"
+nghttp_check_doc 003.html   "detault"
+
+
+################################################################################
+# check retrieving multiple resources from inside a page
+################################################################################
+nghttp_check_assets 001.html "with assets" <<EOF
+$URL_PATH/001.html 251 200
+EOF
+
+nghttp_check_assets 002.jpg "with assets" <<EOF
+$URL_PATH/002.jpg 88K 200
+EOF
+
+nghttp_check_assets 003.html "with assets" <<EOF
+$URL_PATH/003.html 316 200
+$URL_PATH/003/003_img.jpg 88K 200
+EOF
+
+nghttp_check_assets 004.html "with assets" <<EOF
+$URL_PATH/004.html 10K 200
+$URL_PATH/004/gophertiles.jpg 742 200
+$URL_PATH/004/gophertiles_002.jpg 945 200
+$URL_PATH/004/gophertiles_003.jpg 697 200
+$URL_PATH/004/gophertiles_004.jpg 725 200
+$URL_PATH/004/gophertiles_005.jpg 837 200
+$URL_PATH/004/gophertiles_006.jpg 770 200
+$URL_PATH/004/gophertiles_007.jpg 747 200
+$URL_PATH/004/gophertiles_008.jpg 694 200
+$URL_PATH/004/gophertiles_009.jpg 704 200
+$URL_PATH/004/gophertiles_010.jpg 994 200
+$URL_PATH/004/gophertiles_011.jpg 979 200
+$URL_PATH/004/gophertiles_012.jpg 895 200
+$URL_PATH/004/gophertiles_013.jpg 958 200
+$URL_PATH/004/gophertiles_014.jpg 894 200
+$URL_PATH/004/gophertiles_015.jpg 702 200
+$URL_PATH/004/gophertiles_016.jpg 703 200
+$URL_PATH/004/gophertiles_017.jpg 707 200
+$URL_PATH/004/gophertiles_018.jpg 701 200
+$URL_PATH/004/gophertiles_019.jpg 1013 200
+$URL_PATH/004/gophertiles_020.jpg 737 200
+$URL_PATH/004/gophertiles_021.jpg 801 200
+$URL_PATH/004/gophertiles_022.jpg 702 200
+$URL_PATH/004/gophertiles_023.jpg 905 200
+$URL_PATH/004/gophertiles_024.jpg 980 200
+$URL_PATH/004/gophertiles_025.jpg 708 200
+$URL_PATH/004/gophertiles_026.jpg 694 200
+$URL_PATH/004/gophertiles_027.jpg 697 200
+$URL_PATH/004/gophertiles_028.jpg 795 200
+$URL_PATH/004/gophertiles_029.jpg 978 200
+$URL_PATH/004/gophertiles_030.jpg 707 200
+$URL_PATH/004/gophertiles_031.jpg 1K 200
+$URL_PATH/004/gophertiles_032.jpg 688 200
+$URL_PATH/004/gophertiles_033.jpg 701 200
+$URL_PATH/004/gophertiles_034.jpg 898 200
+$URL_PATH/004/gophertiles_035.jpg 986 200
+$URL_PATH/004/gophertiles_036.jpg 770 200
+$URL_PATH/004/gophertiles_037.jpg 959 200
+$URL_PATH/004/gophertiles_038.jpg 936 200
+$URL_PATH/004/gophertiles_039.jpg 700 200
+$URL_PATH/004/gophertiles_040.jpg 784 200
+$URL_PATH/004/gophertiles_041.jpg 758 200
+$URL_PATH/004/gophertiles_042.jpg 796 200
+$URL_PATH/004/gophertiles_043.jpg 813 200
+$URL_PATH/004/gophertiles_044.jpg 924 200
+$URL_PATH/004/gophertiles_045.jpg 978 200
+$URL_PATH/004/gophertiles_046.jpg 752 200
+$URL_PATH/004/gophertiles_047.jpg 751 200
+$URL_PATH/004/gophertiles_048.jpg 737 200
+$URL_PATH/004/gophertiles_049.jpg 992 200
+$URL_PATH/004/gophertiles_050.jpg 688 200
+$URL_PATH/004/gophertiles_051.jpg 697 200
+$URL_PATH/004/gophertiles_052.jpg 699 200
+$URL_PATH/004/gophertiles_053.jpg 1K 200
+$URL_PATH/004/gophertiles_054.jpg 694 200
+$URL_PATH/004/gophertiles_055.jpg 767 200
+$URL_PATH/004/gophertiles_056.jpg 952 200
+$URL_PATH/004/gophertiles_057.jpg 788 200
+$URL_PATH/004/gophertiles_058.jpg 759 200
+$URL_PATH/004/gophertiles_059.jpg 700 200
+$URL_PATH/004/gophertiles_060.jpg 985 200
+$URL_PATH/004/gophertiles_061.jpg 915 200
+$URL_PATH/004/gophertiles_062.jpg 681 200
+$URL_PATH/004/gophertiles_063.jpg 707 200
+$URL_PATH/004/gophertiles_064.jpg 693 200
+$URL_PATH/004/gophertiles_065.jpg 861 200
+$URL_PATH/004/gophertiles_066.jpg 991 200
+$URL_PATH/004/gophertiles_067.jpg 1K 200
+$URL_PATH/004/gophertiles_068.jpg 697 200
+$URL_PATH/004/gophertiles_069.jpg 1K 200
+$URL_PATH/004/gophertiles_070.jpg 1K 200
+$URL_PATH/004/gophertiles_071.jpg 784 200
+$URL_PATH/004/gophertiles_072.jpg 698 200
+$URL_PATH/004/gophertiles_073.jpg 1004 200
+$URL_PATH/004/gophertiles_074.jpg 969 200
+$URL_PATH/004/gophertiles_075.jpg 915 200
+$URL_PATH/004/gophertiles_076.jpg 784 200
+$URL_PATH/004/gophertiles_077.jpg 697 200
+$URL_PATH/004/gophertiles_078.jpg 692 200
+$URL_PATH/004/gophertiles_079.jpg 702 200
+$URL_PATH/004/gophertiles_080.jpg 725 200
+$URL_PATH/004/gophertiles_081.jpg 877 200
+$URL_PATH/004/gophertiles_082.jpg 743 200
+$URL_PATH/004/gophertiles_083.jpg 785 200
+$URL_PATH/004/gophertiles_084.jpg 690 200
+$URL_PATH/004/gophertiles_085.jpg 724 200
+$URL_PATH/004/gophertiles_086.jpg 1K 200
+$URL_PATH/004/gophertiles_087.jpg 883 200
+$URL_PATH/004/gophertiles_088.jpg 702 200
+$URL_PATH/004/gophertiles_089.jpg 693 200
+$URL_PATH/004/gophertiles_090.jpg 947 200
+$URL_PATH/004/gophertiles_091.jpg 959 200
+$URL_PATH/004/gophertiles_092.jpg 736 200
+$URL_PATH/004/gophertiles_093.jpg 806 200
+$URL_PATH/004/gophertiles_094.jpg 820 200
+$URL_PATH/004/gophertiles_095.jpg 918 200
+$URL_PATH/004/gophertiles_096.jpg 689 200
+$URL_PATH/004/gophertiles_097.jpg 796 200
+$URL_PATH/004/gophertiles_098.jpg 686 200
+$URL_PATH/004/gophertiles_099.jpg 698 200
+$URL_PATH/004/gophertiles_100.jpg 686 200
+$URL_PATH/004/gophertiles_101.jpg 686 200
+$URL_PATH/004/gophertiles_102.jpg 682 200
+$URL_PATH/004/gophertiles_103.jpg 703 200
+$URL_PATH/004/gophertiles_104.jpg 698 200
+$URL_PATH/004/gophertiles_105.jpg 702 200
+$URL_PATH/004/gophertiles_106.jpg 989 200
+$URL_PATH/004/gophertiles_107.jpg 720 200
+$URL_PATH/004/gophertiles_108.jpg 834 200
+$URL_PATH/004/gophertiles_109.jpg 756 200
+$URL_PATH/004/gophertiles_110.jpg 703 200
+$URL_PATH/004/gophertiles_111.jpg 815 200
+$URL_PATH/004/gophertiles_112.jpg 780 200
+$URL_PATH/004/gophertiles_113.jpg 992 200
+$URL_PATH/004/gophertiles_114.jpg 862 200
+$URL_PATH/004/gophertiles_115.jpg 1K 200
+$URL_PATH/004/gophertiles_116.jpg 756 200
+$URL_PATH/004/gophertiles_117.jpg 1012 200
+$URL_PATH/004/gophertiles_118.jpg 905 200
+$URL_PATH/004/gophertiles_119.jpg 808 200
+$URL_PATH/004/gophertiles_120.jpg 814 200
+$URL_PATH/004/gophertiles_121.jpg 832 200
+$URL_PATH/004/gophertiles_122.jpg 704 200
+$URL_PATH/004/gophertiles_123.jpg 741 200
+$URL_PATH/004/gophertiles_124.jpg 694 200
+$URL_PATH/004/gophertiles_125.jpg 950 200
+$URL_PATH/004/gophertiles_126.jpg 770 200
+$URL_PATH/004/gophertiles_127.jpg 749 200
+$URL_PATH/004/gophertiles_128.jpg 942 200
+$URL_PATH/004/gophertiles_129.jpg 997 200
+$URL_PATH/004/gophertiles_130.jpg 708 200
+$URL_PATH/004/gophertiles_131.jpg 821 200
+$URL_PATH/004/gophertiles_132.jpg 849 200
+$URL_PATH/004/gophertiles_133.jpg 715 200
+$URL_PATH/004/gophertiles_134.jpg 794 200
+$URL_PATH/004/gophertiles_135.jpg 869 200
+$URL_PATH/004/gophertiles_136.jpg 1K 200
+$URL_PATH/004/gophertiles_137.jpg 757 200
+$URL_PATH/004/gophertiles_138.jpg 991 200
+$URL_PATH/004/gophertiles_139.jpg 704 200
+$URL_PATH/004/gophertiles_140.jpg 707 200
+$URL_PATH/004/gophertiles_141.jpg 959 200
+$URL_PATH/004/gophertiles_142.jpg 691 200
+$URL_PATH/004/gophertiles_143.jpg 921 200
+$URL_PATH/004/gophertiles_144.jpg 932 200
+$URL_PATH/004/gophertiles_145.jpg 696 200
+$URL_PATH/004/gophertiles_146.jpg 711 200
+$URL_PATH/004/gophertiles_147.jpg 817 200
+$URL_PATH/004/gophertiles_148.jpg 966 200
+$URL_PATH/004/gophertiles_149.jpg 1002 200
+$URL_PATH/004/gophertiles_150.jpg 900 200
+$URL_PATH/004/gophertiles_151.jpg 724 200
+$URL_PATH/004/gophertiles_152.jpg 1K 200
+$URL_PATH/004/gophertiles_153.jpg 702 200
+$URL_PATH/004/gophertiles_154.jpg 971 200
+$URL_PATH/004/gophertiles_155.jpg 708 200
+$URL_PATH/004/gophertiles_156.jpg 699 200
+$URL_PATH/004/gophertiles_157.jpg 834 200
+$URL_PATH/004/gophertiles_158.jpg 702 200
+$URL_PATH/004/gophertiles_159.jpg 880 200
+$URL_PATH/004/gophertiles_160.jpg 701 200
+$URL_PATH/004/gophertiles_161.jpg 688 200
+$URL_PATH/004/gophertiles_162.jpg 853 200
+$URL_PATH/004/gophertiles_163.jpg 690 200
+$URL_PATH/004/gophertiles_164.jpg 759 200
+$URL_PATH/004/gophertiles_165.jpg 831 200
+$URL_PATH/004/gophertiles_166.jpg 732 200
+$URL_PATH/004/gophertiles_167.jpg 955 200
+$URL_PATH/004/gophertiles_168.jpg 1K 200
+$URL_PATH/004/gophertiles_169.jpg 969 200
+$URL_PATH/004/gophertiles_170.jpg 701 200
+$URL_PATH/004/gophertiles_171.jpg 755 200
+$URL_PATH/004/gophertiles_172.jpg 924 200
+$URL_PATH/004/gophertiles_173.jpg 958 200
+$URL_PATH/004/gophertiles_174.jpg 998 200
+$URL_PATH/004/gophertiles_175.jpg 702 200
+$URL_PATH/004/gophertiles_176.jpg 760 200
+$URL_PATH/004/gophertiles_177.jpg 732 200
+$URL_PATH/004/gophertiles_178.jpg 929 200
+$URL_PATH/004/gophertiles_179.jpg 712 200
+$URL_PATH/004/gophertiles_180.jpg 1013 200
+EOF
+
+nghttp_check_assets 005.txt "with assets" <<EOF
+$URL_PATH/005.txt 9M 200
+EOF
+
+nghttp_check_assets 006.html "with assets" <<EOF
+$URL_PATH/006.html 543 200
+$URL_PATH/006/006.css 216 200
+$URL_PATH/006/006.js 839 200
+EOF
+
+nghttp_check_assets 007.html "with assets" <<EOF
+$URL_PATH/007.html 808 200
+EOF
+
+nghttp_check_assets upload.py "with assets" <<EOF
+$URL_PATH/upload.py 219 200
+EOF
+
+#nghttp_check_assets 009.php "with assets" <<EOF
+#EOF
+#
+################################################################################
+# check different window sizes
+################################################################################
+nghttp_check_assets 003.html "with assets" --window-bits=24 <<EOF
+$URL_PATH/003.html 316 200
+$URL_PATH/003/003_img.jpg 88K 200
+EOF
+
+################################################################################
+# check cgi generated content
+################################################################################
+
+if [ "$URL_SCHEME" = "https" ]; then
+    CONTENT="<html>
+<body>
+<h2>Hello World!</h2>
+SSL_PROTOCOL=TLSv1.2
+</body>
+</html>"
+else
+    CONTENT="<html>
+<body>
+<h2>Hello World!</h2>
+SSL_PROTOCOL=
+</body>
+</html>"
+fi
+
+nghttp_check_content hello.py "get hello.py"   <<EOF
+$CONTENT
+EOF
+
diff --git a/modules/http2/sandbox/test/test_nghttp_post.sh b/modules/http2/sandbox/test/test_nghttp_post.sh
new file mode 100644 (file)
index 0000000..ea8a4bc
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source test_common.sh
+echo "nghttp POST on: $@"
+
+CHR100="012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
+"
+
+if [ ! -f $GEN/data-1k ]; then
+    i=0; while [ $i -lt 10 ]; do
+        echo -n "$CHR100"
+        i=$[ i + 1 ]
+    done > $GEN/data-1k
+fi
+
+if [ ! -f $GEN/data-10k ]; then
+    i=0; while [ $i -lt 10 ]; do
+        cat $GEN/data-1k
+        i=$[ i + 1 ]
+    done  > $GEN/data-10k
+fi
+
+if [ ! -f $GEN/data-100k ]; then
+    i=0; while [ $i -lt 10 ]; do
+        cat $GEN/data-10k
+        i=$[ i + 1 ]
+    done > $GEN/data-100k
+fi
+
+if [ ! -f $GEN/data-1m ]; then
+    i=0; while [ $i -lt 10 ]; do
+        cat $GEN/data-100k
+        i=$[ i + 1 ]
+    done > $GEN/data-1m
+fi
+
+# Tests witht the nghttp client that *requires* h2/h2c. Sends "OPTIONS *"
+# on h2c which is a good test.
+#
+nghttp_remove_file upload.py data-1k  "rm data-1k"
+nghttp_post_file upload.py $GEN/data-1k   "1k upload"
+nghttp_remove_file upload.py data-10k  "rm data-10k"
+nghttp_post_file upload.py $GEN/data-10k  "10k upload"
+nghttp_remove_file upload.py data-100k  "rm data-100k"
+nghttp_post_file upload.py $GEN/data-100k "100k upload"
+nghttp_remove_file upload.py data-1m  "rm data-1m"
+nghttp_post_file upload.py $GEN/data-1m   "1m upload"
+
+# Tests without content-length announced
+nghttp_remove_file upload.py data-1k  "rm data-1k"
+nghttp_post_file upload.py $GEN/data-1k   "1k upload w/o c-len" --no-content-length
+nghttp_remove_file upload.py data-10k  "rm data-10k"
+nghttp_post_file upload.py $GEN/data-10k  "10k upload w/o c-len" --no-content-length
+nghttp_remove_file upload.py data-100k  "rm data-100k"
+nghttp_post_file upload.py $GEN/data-100k "100k upload w/o c-len" --no-content-length
+nghttp_remove_file upload.py data-1m  "rm data-1m"
+nghttp_post_file upload.py $GEN/data-1m   "1m upload w/o c-len" --no-content-length
+
+
+
+
diff --git a/modules/http2/setup/Makefile.am b/modules/http2/setup/Makefile.am
new file mode 100644 (file)
index 0000000..da197f9
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+SUBDIRS =
+
+EXTRA_DIST = \
+    h2.load \
+    h2.conf \
+    install-config.sh
+
+
+ACLOCAL_AMFLAGS = -I m4
+
+SYSCONF_DIR = @SYSCONF_DIR@
+
+# overwrite install target
+install:
+       @bash install-config.sh $(SYSCONF_DIR) $(DESTDIR)
diff --git a/modules/http2/setup/h2.conf b/modules/http2/setup/h2.conf
new file mode 100644 (file)
index 0000000..c38cc66
--- /dev/null
@@ -0,0 +1 @@
+H2Engine on
diff --git a/modules/http2/setup/h2.load b/modules/http2/setup/h2.load
new file mode 100644 (file)
index 0000000..f4639db
--- /dev/null
@@ -0,0 +1 @@
+LoadModule h2_module modules/mod_h2.so
diff --git a/modules/http2/setup/install-config.sh b/modules/http2/setup/install-config.sh
new file mode 100644 (file)
index 0000000..8a7ec1e
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SYSCONF="$1"
+DESTDIR="$2"
+A2ENMOD="$( type -p a2enmod )"
+
+INSTALL_LOC=""
+
+if [ -d "$DESTDIR" ]; then
+    INSTALL_LOC="$DESTDIR/$SYSCONF"
+else
+    INSTALL_LOC="$SYSCONF"
+fi
+echo "[DEBUG] Install location is assumed to be $INSTALL_LOC"
+if [ -d "$SYSCONF/mods-available" ]; then
+    echo -n "Debian layout assumed, installing mod_h2 config in $INSTALL_LOC..."
+    cp h2.conf h2.load "$INSTALL_LOC/mods-available"
+    echo "done."
+    if [ -x "$A2ENMOD" ] && [ ! -d "$DESTDIR" ]; then
+        echo -n "enabling mod_h2..."
+        "$A2ENMOD" mod_h2
+        echo "done."
+    fi
+elif [ -d "$SYSCONF/../conf.d" ] && [ -d "$SYSCONF/../conf.modules.d" ]; then
+    # Odds are this is a Fedora box!
+    echo -n "RHEL/Fedora layout assumed, installing mod_h2 config in $INSTALL_LOC..."
+    cp h2.conf "$INSTALL_LOC/../conf.d"
+    cp h2.load "$INSTALL_LOC/../conf.modules.d/10-h2.conf"
+    echo "done."
+else
+    cat <<EOF
+  This does not look like a apache2 installation, as in Ubuntu or
+  other debian based systems. Therefore, the local files h2.load and
+  h2.conf have *not* been installed.
+
+  If you want to have the h2 module enabled in your apache installtion, you
+  need to add
+     LoadModule h2_module modules/mod_h2.so
+  somewhere in your config files and add a line like
+     H2Engine on
+  whereever you want the module to be active (general server of specific
+  virtual hosts).
+
+EOF
+fi
+